From c1c29efcf032f4b6fc4ba8901acc06e955b56ab3 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Thu, 13 Mar 2025 14:39:24 +0100 Subject: [PATCH] Implement joystick space ship control --- engine/src/engine/engine_create_info.rs | 1 - engine/src/engine/engine_settings.rs | 1 - .../content/components/free_space_control.rs | 88 +++++++--- .../rendering/raytracer/hardwareraytracer.rs | 10 +- examples/free_space/src/game.rs | 166 ++++++++++++++---- examples/free_space/src/main.rs | 11 +- presentation/src/input/eventsystem.rs | 26 +++ 7 files changed, 228 insertions(+), 75 deletions(-) diff --git a/engine/src/engine/engine_create_info.rs b/engine/src/engine/engine_create_info.rs index 2ed40c8..17616f8 100644 --- a/engine/src/engine/engine_create_info.rs +++ b/engine/src/engine/engine_create_info.rs @@ -81,7 +81,6 @@ impl<'a> Default for EngineCreateInfo<'a> { stack_size: 32, max_lights: 32, - recursion_depth: 3, use_default_pipeline: true, max_samplers: 1024, }, diff --git a/engine/src/engine/engine_settings.rs b/engine/src/engine/engine_settings.rs index ec2b9c7..18c7ac6 100644 --- a/engine/src/engine/engine_settings.rs +++ b/engine/src/engine/engine_settings.rs @@ -36,7 +36,6 @@ pub struct RaytracingInfo { // hardware raytracer pub max_lights: u32, - pub recursion_depth: u32, pub use_default_pipeline: bool, diff --git a/engine/src/scene/content/components/free_space_control.rs b/engine/src/scene/content/components/free_space_control.rs index c8cac95..5eaf2ad 100644 --- a/engine/src/scene/content/components/free_space_control.rs +++ b/engine/src/scene/content/components/free_space_control.rs @@ -39,6 +39,7 @@ impl Default for FreeSpaceControlSettings { pub struct FreeSpaceControl { settings: FreeSpaceControlSettings, + dead_zone: f32, /// (yaw, pitch, roll) rotation: Vector3>, @@ -53,13 +54,21 @@ pub struct FreeSpaceControl { /// (forward, sideward, upward) input_position: Vector3, + /// (yaw, pitch, roll) + velocity_rotation: Vector3>, + /// (forward, sideward, upward) + velocity_position: Vector3, + last_update: Option, } impl FreeSpaceControl { - pub fn new(settings: FreeSpaceControlSettings) -> Self { + const EPSILON: f32 = 0.001; + + pub fn new(dead_zone: f32, settings: FreeSpaceControlSettings) -> Self { Self { settings, + dead_zone, rotation: vec3(Deg(0.0), Deg(0.0), Deg(0.0)), position: Vector3::zero(), @@ -71,6 +80,9 @@ impl FreeSpaceControl { input_rotation: Vector3::zero(), input_position: Vector3::zero(), + velocity_rotation: vec3(Deg(0.0), Deg(0.0), Deg(0.0)), + velocity_position: Vector3::zero(), + last_update: None, } } @@ -80,51 +92,71 @@ impl FreeSpaceControl { } pub fn set_throttle(&mut self, throttle: f32) { - debug_assert!(-1.0 <= throttle); - debug_assert!(throttle <= 1.0); + debug_assert!(throttle.abs() <= (1.0 + Self::EPSILON), "value {throttle}"); - self.input_position.y = throttle; + self.input_position.y = if throttle.abs() < self.dead_zone { + 0.0 + } else { + throttle.clamp(-1.0, 1.0) + }; } pub fn set_left_right_strafe(&mut self, strafe: f32) { - debug_assert!(-1.0 <= strafe); - debug_assert!(strafe <= 1.0); + debug_assert!(strafe.abs() <= (1.0 + Self::EPSILON), "value {strafe}"); - self.input_position.x = strafe; + self.input_position.x = if strafe.abs() < self.dead_zone { + 0.0 + } else { + strafe.clamp(-1.0, 1.0) + }; } pub fn set_up_down_strafe(&mut self, strafe: f32) { - debug_assert!(-1.0 <= strafe); - debug_assert!(strafe <= 1.0); + debug_assert!(strafe.abs() <= (1.0 + Self::EPSILON), "value {strafe}"); - self.input_position.z = strafe; + self.input_position.z = if strafe.abs() < self.dead_zone { + 0.0 + } else { + strafe.clamp(-1.0, 1.0) + }; } pub fn set_yaw(&mut self, yaw: f32) { - debug_assert!(-1.0 <= yaw); - debug_assert!(yaw <= 1.0); + debug_assert!(yaw.abs() <= (1.0 + Self::EPSILON), "value {yaw}"); - self.input_rotation.x = yaw; + self.input_rotation.x = if yaw.abs() < self.dead_zone { + 0.0 + } else { + yaw.clamp(-1.0, 1.0) + }; } pub fn set_pitch(&mut self, pitch: f32) { - debug_assert!(-1.0 <= pitch); - debug_assert!(pitch <= 1.0); + debug_assert!(pitch.abs() <= (1.0 + Self::EPSILON), "value {pitch}"); - self.input_rotation.y = pitch; + self.input_rotation.y = if pitch.abs() < self.dead_zone { + 0.0 + } else { + pitch.clamp(-1.0, 1.0) + }; } pub fn set_roll(&mut self, roll: f32) { - debug_assert!(-1.0 <= roll); - debug_assert!(roll <= 1.0); + debug_assert!(roll.abs() <= (1.0 + Self::EPSILON), "value {roll}"); - self.input_rotation.z = roll; + self.input_rotation.z = if roll.abs() < self.dead_zone { + 0.0 + } else { + roll.clamp(-1.0, 1.0) + }; } - fn add_rotation(&mut self, rotation: Vector3>) { - self.rotation.x += rotation.x; - self.rotation.y += rotation.y; - self.rotation.z += rotation.z; + fn add_rotation(lhs: Vector3>, rhs: Vector3>) -> Vector3> { + vec3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z) + } + + fn factor(f: f32, v: Vector3>) -> Vector3> { + vec3(Deg(f * v.x.0), Deg(f * v.y.0), Deg(f * v.z.0)) } fn rotation_matrix(&self) -> Matrix3 { @@ -150,8 +182,14 @@ impl FreeSpaceControl { let (rotation_changes, position_changes) = self.calculate_tick_changes(diff); let rotated_position_changes = self.rotation_matrix() * position_changes; - self.add_rotation(rotation_changes); - self.position += rotated_position_changes; + self.velocity_rotation = Self::add_rotation(self.velocity_rotation, rotation_changes); + self.velocity_position += rotated_position_changes; + + self.position += diff.as_secs_f32() * self.velocity_position; + self.rotation = Self::add_rotation( + self.rotation, + Self::factor(diff.as_secs_f32(), self.velocity_rotation), + ); self.current_translation = Matrix4::from_translation(self.position); self.current_rotation = self.rotation_matrix(); diff --git a/engine/src/scene/rendering/raytracer/hardwareraytracer.rs b/engine/src/scene/rendering/raytracer/hardwareraytracer.rs index 298fa23..ddd18f3 100644 --- a/engine/src/scene/rendering/raytracer/hardwareraytracer.rs +++ b/engine/src/scene/rendering/raytracer/hardwareraytracer.rs @@ -1,9 +1,9 @@ use crate::prelude::*; use super::{ + RenderingCore, animator::*, raytraceshader::{DefaultHardwareRayTracingShader, RTInAWeekendShader}, - RenderingCore, }; use anyhow::Result; @@ -136,10 +136,10 @@ impl RenderingCore for HardwareRayTracer { let pipeline_layout = pipeline_layout_builder.build(device.clone())?; - let recursion_depth = RayTracingPipelineBuilder::check_max_recursion( - device, - ray_tracing_info.recursion_depth, - ); + let recursion_depth = device + .physical_device() + .ray_tracing_properties() + .maxRayRecursionDepth; let (ray_tracing_pipeline, sbt) = Self::create_pipeline( ray_tracing_info.use_default_pipeline, diff --git a/examples/free_space/src/game.rs b/examples/free_space/src/game.rs index 427924d..a3f2621 100644 --- a/examples/free_space/src/game.rs +++ b/examples/free_space/src/game.rs @@ -8,6 +8,8 @@ use engine::prelude::{ *, }; +use crate::FREE_CAMERA_CONTROL; + #[derive(Clone, Copy, Debug)] struct PlayerEntity(Entity); @@ -37,7 +39,7 @@ enum Control { #[derive(Clone, Debug)] struct InputSettings { - mappings: HashMap<(String, Input), Control>, + mappings: HashMap<(String, u32, Input), (Control, bool)>, } impl Default for InputSettings { @@ -45,27 +47,44 @@ impl Default for InputSettings { Self { mappings: [ ( - ("Joystick 1".to_string(), Input::Axis(0)), - Control::Throttle, + ("Thrustmaster T.16000M".to_string(), 1, Input::Axis(0)), + (Control::Pitch, false), ), ( - ("Joystick 1".to_string(), Input::Axis(1)), - Control::StrafeHorizontal, + ("Thrustmaster T.16000M".to_string(), 1, Input::Axis(1)), + (Control::Throttle, false), ), ( - ("Joystick 1".to_string(), Input::Axis(2)), - Control::StrafeVertical, + ("Thrustmaster T.16000M".to_string(), 1, Input::Axis(2)), + (Control::StrafeHorizontal, false), ), - (("Joystick 1".to_string(), Input::Axis(3)), Control::Pitch), ( - ("Joystick 1".to_string(), Input::Button(Button::One)), - Control::PrimaryWeapon, + ("Thrustmaster T.16000M".to_string(), 1, Input::Axis(3)), + (Control::StrafeVertical, false), ), - (("Joystick 2".to_string(), Input::Axis(1)), Control::Yaw), - (("Joystick 2".to_string(), Input::Axis(0)), Control::Roll), ( - ("Joystick 2".to_string(), Input::Button(Button::One)), - Control::SecondaryWeapon, + ( + "Thrustmaster T.16000M".to_string(), + 1, + Input::Button(Button::One), + ), + (Control::PrimaryWeapon, false), + ), + ( + ("Thrustmaster T.16000M".to_string(), 0, Input::Axis(0)), + (Control::Yaw, true), + ), + ( + ("Thrustmaster T.16000M".to_string(), 0, Input::Axis(1)), + (Control::Roll, false), + ), + ( + ( + "Thrustmaster T.16000M".to_string(), + 0, + Input::Button(Button::One), + ), + (Control::SecondaryWeapon, false), ), ] .into_iter() @@ -75,9 +94,14 @@ impl Default for InputSettings { } impl InputSettings { - pub fn map_axis(&self, device_name: impl ToString, axis: u8) -> Option { + pub fn map_axis( + &self, + device_name: impl ToString, + device_id: u32, + axis: u8, + ) -> Option<(Control, bool)> { self.mappings - .get(&(device_name.to_string(), Input::Axis(axis))) + .get(&(device_name.to_string(), device_id, Input::Axis(axis))) .map(|control| *control) } } @@ -86,67 +110,124 @@ pub struct Game; impl Game { pub fn update(&mut self, world: &mut World) -> Result<()> { - let now = world.now(); - let mut resources = world.resources.multi_mut(); - let scene = resources.get::(); - let camera_control = resources.get::(); + if FREE_CAMERA_CONTROL { + let now = world.now(); + let mut resources = world.resources.multi_mut(); + let scene = resources.get::(); + let camera_control = resources.get::(); - camera_control.update(now, scene.view_mut())?; + camera_control.update(now, scene.view_mut())?; + } Ok(()) } pub fn event(&mut self, world: &mut World, event: EngineEvent<'_>) -> Result<()> { - let player = world.resources.get::(); - let (fighter_object, resources) = world.entity_resources(player.0)?; + if let Some(event) = Self::motion_concepts(world, event)? { + match event { + EngineEvent::JoystickAdded(joystick) => { + println!("joystick {} added", joystick.name()); + } + EngineEvent::JoystickRemoved(joystick) => { + println!("joystick {} removed", joystick.name()); + } + + _ => (), + } + } + + Ok(()) + } + + fn motion_concepts<'a>( + world: &mut World, + event: EngineEvent<'a>, + ) -> Result>> { + if FREE_CAMERA_CONTROL { + Self::free_camera(world, event) + } else { + Self::joystick_movement(world, event) + } + } + + fn free_camera<'a>( + world: &mut World, + event: EngineEvent<'a>, + ) -> Result>> { match event { - EngineEvent::MouseButtonDown(MouseButton::Left) => { - let camera_control = resources.get_mut::(); + EngineEvent::MouseButtonDown(MouseButton::Middle) => { + let camera_control = world.resources.get_mut::(); camera_control.mouse_down(); } - EngineEvent::MouseButtonUp(MouseButton::Left) => { - let camera_control = resources.get_mut::(); + EngineEvent::MouseButtonUp(MouseButton::Middle) => { + let camera_control = world.resources.get_mut::(); camera_control.mouse_release(); } EngineEvent::MouseMotion(x, y) => { - let mut resources = resources.multi_mut(); + let mut resources = world.resources.multi_mut(); let scene = resources.get::(); let camera_control = resources.get::(); camera_control.mouse_move(x, y, scene.view_mut())?; + + return Ok(Some(event)); } EngineEvent::KeyDown(key) => { - let camera_control = resources.get_mut::(); + let camera_control = world.resources.get_mut::(); match key { Keycode::W => camera_control.forward_back(1.0), Keycode::A => camera_control.left_right(-1.0), Keycode::S => camera_control.forward_back(-1.0), Keycode::D => camera_control.left_right(1.0), + Keycode::Space => camera_control.up_down(1.0), + Keycode::LCtrl => camera_control.up_down(-1.0), - _ => (), + _ => return Ok(Some(event)), } } EngineEvent::KeyUp(key) => { - let camera_control = resources.get_mut::(); + let camera_control = world.resources.get_mut::(); match key { Keycode::W => camera_control.forward_back(-1.0), Keycode::A => camera_control.left_right(1.0), Keycode::S => camera_control.forward_back(1.0), Keycode::D => camera_control.left_right(-1.0), + Keycode::Space => camera_control.up_down(-1.0), + Keycode::LCtrl => camera_control.up_down(1.0), - _ => (), + _ => return Ok(Some(event)), } } + _ => return Ok(Some(event)), + } + + Ok(None) + } + + fn joystick_movement<'a>( + world: &mut World, + event: EngineEvent<'a>, + ) -> Result>> { + let player = world.resources.get::(); + let (fighter_object, resources) = world.entity_resources(player.0)?; + + match event { EngineEvent::JoystickAxis(joystick, axis_index, value) => { - let normalized = value as f32 * (i16::MAX as f32).recip(); + let mut normalized = value as f32 * (i16::MAX as f32).recip(); let input_settings = resources.get::(); let player_control = fighter_object.get_component_mut::()?; - if let Some(control) = input_settings.map_axis(joystick.name(), axis_index) { + if let Some((control, inverted)) = + input_settings.map_axis(joystick.name(), joystick.id(), axis_index) + { + if inverted { + normalized = -normalized + }; + match control { Control::Throttle => player_control.set_throttle(normalized), Control::StrafeHorizontal => { @@ -163,10 +244,10 @@ impl Game { } } - _ => (), + _ => return Ok(Some(event)), } - Ok(()) + Ok(None) } } @@ -179,14 +260,19 @@ impl Game { EmptyFilter, )?; - // world_builder.add_update("camera_position", 1_000, Self::camera_update, EmptyFilter)?; + if !FREE_CAMERA_CONTROL { + world_builder.add_update("camera_position", 1_000, Self::camera_update, EmptyFilter)?; + } Ok(()) } pub fn setup_scene(world: &mut World) -> Result<()> { let mut fighter = AssetHandler::create(world).create_entity("fighter_edited")?; - fighter.insert_component(FreeSpaceControl::new(FreeSpaceControlSettings::default())); + fighter.insert_component(FreeSpaceControl::new( + 0.02, + FreeSpaceControlSettings::default(), + )); let player = PlayerEntity(world.add_entity(fighter)?); world.resources.insert(player); @@ -230,7 +316,9 @@ impl Game { view.camera_mut() .set_center((control.translation() * DEFAULT_CENTER).truncate()); view.camera_mut() - .set_eye_dir(control.rotation() * Vector3::unit_x()); + .set_eye_dir(control.rotation() * Vector3::unit_y()); + view.camera_mut() + .set_up(control.rotation() * Vector3::unit_z()); view.update_buffer() } diff --git a/examples/free_space/src/main.rs b/examples/free_space/src/main.rs index 9cd8848..e601ae2 100644 --- a/examples/free_space/src/main.rs +++ b/examples/free_space/src/main.rs @@ -11,13 +11,14 @@ use game::Game; use game_state::GameState; use skybox::SkyBox; +const FREE_CAMERA_CONTROL: bool = false; + fn main() -> Result<()> { let mut world_builder = World::builder(); let mut engine_ci = EngineCreateInfo::default(); engine_ci.resource_base_path = "/home/michaelh/Sync/space_game/".to_string(); engine_ci.asset_directories.gltf_file_directory = "objects".into(); - engine_ci.raytracing_info.recursion_depth = 30; Engine::new::(engine_ci, &mut world_builder)?; @@ -38,9 +39,11 @@ fn main() -> Result<()> { .into_iter(), )?; - let view = world_builder.resources.get_mut::().view_mut(); - let camera_control = FreeCameraControl::new(view)?; - world_builder.resources.insert(camera_control); + if FREE_CAMERA_CONTROL { + let view = world_builder.resources.get_mut::().view_mut(); + let camera_control = FreeCameraControl::new(view)?; + world_builder.resources.insert(camera_control); + } Game::setup_updates(&mut world_builder)?; let mut world = world_builder.build(); diff --git a/presentation/src/input/eventsystem.rs b/presentation/src/input/eventsystem.rs index 4eef87b..d19997c 100644 --- a/presentation/src/input/eventsystem.rs +++ b/presentation/src/input/eventsystem.rs @@ -116,6 +116,32 @@ impl EventSystem { connected_joysticks: HashMap::new(), }; + event_system.connected_joysticks = (0..event_system + .joystick_subsystem + .num_joysticks() + .map_err(|s| anyhow::Error::msg(s))?) + .into_iter() + .map(|i| Ok((i, Joystick::new(&event_system.joystick_subsystem, i)?))) + .collect::>>()?; + + event_system.connected_controllers = (0..event_system + .controller_subsystem + .num_joysticks() + .map_err(|s| anyhow::Error::msg(s))?) + .into_iter() + .filter(|i| event_system.controller_subsystem.is_game_controller(*i)) + .map(|i| { + Ok(( + i, + Controller::new( + &event_system.controller_subsystem, + i, + event_system.controller_deadzones.clone(), + )?, + )) + }) + .collect::>>()?; + event_system.disable_mouse(); event_system.disable_keyboard(); event_system.disable_controller();