Add raidillon_input and FPSCameraController
This commit is contained in:
parent
d0440f3da3
commit
97195fbd05
7 changed files with 268 additions and 0 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -1748,11 +1748,20 @@ dependencies = [
|
|||
"hecs",
|
||||
"raidillon_core",
|
||||
"raidillon_ecs",
|
||||
"raidillon_input",
|
||||
"raidillon_render",
|
||||
"raidillon_ui",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raidillon_input"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raidillon_render"
|
||||
version = "0.1.0"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ members = [
|
|||
"raidillon_render",
|
||||
"raidillon_ui",
|
||||
"raidillon_game",
|
||||
"raidillon_input",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,3 +14,4 @@ raidillon_ecs = { path = "../raidillon_ecs" }
|
|||
raidillon_ui = { path = "../raidillon_ui" }
|
||||
raidillon_core = { path = "../raidillon_core" }
|
||||
hecs = "0.10.5"
|
||||
raidillon_input = { path = "../raidillon_input" }
|
||||
|
|
|
|||
|
|
@ -5,6 +5,17 @@ use raidillon_core::Time;
|
|||
use raidillon_ecs::Transform;
|
||||
use raidillon_render::{Camera, GliumRenderer, gltf_loader, ECSRenderer};
|
||||
use raidillon_ui::Gui;
|
||||
use raidillon_input::{Input, FPSCameraController};
|
||||
use winit::keyboard::KeyCode;
|
||||
use winit::window::CursorGrabMode;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
enum Action {
|
||||
MoveForward,
|
||||
MoveBackward,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let event_loop = glium::winit::event_loop::EventLoop::builder()
|
||||
|
|
@ -26,6 +37,17 @@ fn main() -> Result<()> {
|
|||
// Dear ImGui integration
|
||||
let mut gui = Gui::new(&display, &window)?;
|
||||
|
||||
let mut input = Input::<Action>::new();
|
||||
input.map_key(KeyCode::KeyW, Action::MoveForward);
|
||||
input.map_key(KeyCode::KeyS, Action::MoveBackward);
|
||||
input.map_key(KeyCode::KeyA, Action::MoveLeft);
|
||||
input.map_key(KeyCode::KeyD, Action::MoveRight);
|
||||
|
||||
let mut camera_controller = FPSCameraController::new(Vec3::new(0.0, 0.0, 2.0));
|
||||
|
||||
let mut cursor_grabbed = false;
|
||||
let mut attempted_initial_grab = false;
|
||||
|
||||
let mut time = Time::new();
|
||||
|
||||
let object_ent = {
|
||||
|
|
@ -66,6 +88,8 @@ fn main() -> Result<()> {
|
|||
|
||||
gui.handle_event(&window, &event);
|
||||
|
||||
input.handle_event(&event);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => el.exit(),
|
||||
|
|
@ -110,6 +134,51 @@ fn main() -> Result<()> {
|
|||
},
|
||||
Event::AboutToWait => {
|
||||
time.tick();
|
||||
|
||||
if !attempted_initial_grab {
|
||||
attempted_initial_grab = true;
|
||||
if window
|
||||
.set_cursor_grab(CursorGrabMode::Confined)
|
||||
.or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked))
|
||||
.is_ok()
|
||||
{
|
||||
window.set_cursor_visible(false);
|
||||
cursor_grabbed = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let dt = time.delta_seconds();
|
||||
camera_controller.update(
|
||||
&input,
|
||||
dt,
|
||||
cursor_grabbed,
|
||||
(Action::MoveForward, Action::MoveBackward, Action::MoveLeft, Action::MoveRight),
|
||||
);
|
||||
|
||||
if input.key_pressed(KeyCode::Escape) {
|
||||
if cursor_grabbed {
|
||||
let _ = window.set_cursor_grab(CursorGrabMode::None);
|
||||
window.set_cursor_visible(true);
|
||||
cursor_grabbed = false;
|
||||
} else if window
|
||||
.set_cursor_grab(CursorGrabMode::Confined)
|
||||
.or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked))
|
||||
.is_ok()
|
||||
{
|
||||
window.set_cursor_visible(false);
|
||||
cursor_grabbed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut cam) = ecsr.world.query_one_mut::<&mut Camera>(camera_ent) {
|
||||
cam.eye = camera_controller.position;
|
||||
cam.center = camera_controller.position + camera_controller.front();
|
||||
}
|
||||
}
|
||||
|
||||
input.end_frame();
|
||||
|
||||
gui.prepare_frame(&window);
|
||||
window.request_redraw();
|
||||
}
|
||||
|
|
|
|||
8
raidillon_input/Cargo.toml
Normal file
8
raidillon_input/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "raidillon_input"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
winit = "0.30"
|
||||
glam = "0.30.4"
|
||||
73
raidillon_input/src/camera.rs
Normal file
73
raidillon_input/src/camera.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use glam::Vec3;
|
||||
use std::hash::Hash;
|
||||
|
||||
use super::Input;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FPSCameraController {
|
||||
pub position: Vec3,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
pub speed: f32,
|
||||
pub sensitivity: f32,
|
||||
}
|
||||
|
||||
impl FPSCameraController {
|
||||
pub fn new(position: Vec3) -> Self {
|
||||
Self {
|
||||
position,
|
||||
yaw: -90.0,
|
||||
pitch: 0.0,
|
||||
speed: 3.0,
|
||||
sensitivity: 0.1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update<A>(&mut self,
|
||||
input: &Input<A>,
|
||||
dt: f32,
|
||||
mouse_enabled: bool,
|
||||
actions: (A, A, A, A))
|
||||
where
|
||||
A: Copy + Eq + Hash,
|
||||
{
|
||||
let (forward, backward, left, right) = actions;
|
||||
|
||||
// Mouse look
|
||||
if mouse_enabled {
|
||||
let (dx, dy) = input.mouse_delta();
|
||||
self.yaw += dx as f32 * self.sensitivity;
|
||||
self.pitch -= dy as f32 * self.sensitivity;
|
||||
self.pitch = self.pitch.clamp(-89.0, 89.0);
|
||||
}
|
||||
|
||||
// Movement
|
||||
let front = self.front();
|
||||
let right_vec = front.cross(Vec3::Y).normalize();
|
||||
let frame_speed = self.speed * dt;
|
||||
|
||||
if input.action_held(forward) {
|
||||
self.position += front * frame_speed;
|
||||
}
|
||||
if input.action_held(backward) {
|
||||
self.position -= front * frame_speed;
|
||||
}
|
||||
if input.action_held(left) {
|
||||
self.position -= right_vec * frame_speed;
|
||||
}
|
||||
if input.action_held(right) {
|
||||
self.position += right_vec * frame_speed;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn front(&self) -> Vec3 {
|
||||
let yaw_rad = self.yaw.to_radians();
|
||||
let pitch_rad = self.pitch.to_radians();
|
||||
Vec3::new(
|
||||
yaw_rad.cos() * pitch_rad.cos(),
|
||||
pitch_rad.sin(),
|
||||
yaw_rad.sin() * pitch_rad.cos(),
|
||||
)
|
||||
.normalize()
|
||||
}
|
||||
}
|
||||
107
raidillon_input/src/lib.rs
Normal file
107
raidillon_input/src/lib.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::Hash;
|
||||
|
||||
use winit::event::{DeviceEvent, ElementState, Event, WindowEvent};
|
||||
use winit::keyboard::{KeyCode, PhysicalKey};
|
||||
|
||||
pub mod camera;
|
||||
pub use camera::FPSCameraController;
|
||||
|
||||
pub struct Input<A: Copy + Eq + Hash> {
|
||||
pressed_keys: HashSet<KeyCode>,
|
||||
pressed_once: HashSet<KeyCode>,
|
||||
|
||||
keymap: HashMap<KeyCode, A>,
|
||||
pressed_actions: HashSet<A>,
|
||||
pressed_actions_once: HashSet<A>,
|
||||
|
||||
mouse_delta: (f64, f64),
|
||||
}
|
||||
|
||||
impl<A: Copy + Eq + Hash> Input<A> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pressed_keys: HashSet::new(),
|
||||
pressed_once: HashSet::new(),
|
||||
keymap: HashMap::new(),
|
||||
pressed_actions: HashSet::new(),
|
||||
pressed_actions_once: HashSet::new(),
|
||||
mouse_delta: (0.0, 0.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_key(&mut self, key: KeyCode, action: A) {
|
||||
self.keymap.insert(key, action);
|
||||
}
|
||||
|
||||
pub fn clear_keymap(&mut self) {
|
||||
self.keymap.clear();
|
||||
self.pressed_actions.clear();
|
||||
self.pressed_actions_once.clear();
|
||||
}
|
||||
|
||||
pub fn handle_event<T>(&mut self, event: &Event<T>) {
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
let key_code = match event.physical_key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
match event.state {
|
||||
ElementState::Pressed => {
|
||||
self.pressed_keys.insert(key_code);
|
||||
self.pressed_once.insert(key_code);
|
||||
|
||||
if let Some(&action) = self.keymap.get(&key_code) {
|
||||
self.pressed_actions.insert(action);
|
||||
self.pressed_actions_once.insert(action);
|
||||
}
|
||||
}
|
||||
ElementState::Released => {
|
||||
self.pressed_keys.remove(&key_code);
|
||||
|
||||
if let Some(&action) = self.keymap.get(&key_code) {
|
||||
self.pressed_actions.remove(&action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
DeviceEvent::MouseMotion { delta } => {
|
||||
self.mouse_delta.0 += delta.0;
|
||||
self.mouse_delta.1 += delta.1;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_held(&self, key: KeyCode) -> bool {
|
||||
self.pressed_keys.contains(&key)
|
||||
}
|
||||
pub fn key_pressed(&self, key: KeyCode) -> bool {
|
||||
self.pressed_once.contains(&key)
|
||||
}
|
||||
|
||||
pub fn action_held(&self, action: A) -> bool {
|
||||
self.pressed_actions.contains(&action)
|
||||
}
|
||||
pub fn action_pressed(&self, action: A) -> bool {
|
||||
self.pressed_actions_once.contains(&action)
|
||||
}
|
||||
|
||||
pub fn mouse_delta(&self) -> (f64, f64) {
|
||||
self.mouse_delta
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
self.mouse_delta = (0.0, 0.0);
|
||||
self.pressed_once.clear();
|
||||
self.pressed_actions_once.clear();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue