Rotate camera with mouse
ci/woodpecker/push/build Pipeline was successful Details

This commit is contained in:
SeanOMik 2023-10-25 21:49:38 -04:00
parent 249b87afed
commit f3c25b6370
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
7 changed files with 175 additions and 97 deletions

View File

@ -1,14 +1,21 @@
use std::ops::Deref;
use edict::{World, Component};
use lyra_engine::{math::{Vec3, Angle, Quat}, input::{InputButtons, KeyCode, MouseMotion}, ecs::{SimpleSystem, components::camera::CameraComponent, EventQueue}, game::Game, plugin::Plugin};
use edict::{Component, World};
use lyra_engine::{
ecs::{components::camera::CameraComponent, EventQueue, SimpleSystem},
game::Game,
input::{InputButtons, KeyCode, MouseMotion},
math::{Angle, Quat, Vec3},
plugin::Plugin,
};
use tracing::debug;
#[derive(Clone, Component)]
pub struct FreeFlyCamera {
pub speed: f32,
pub look_speed: f32,
pub look_with_keys: bool
pub mouse_sensitivity: f32,
pub look_with_keys: bool,
}
impl Default for FreeFlyCamera {
@ -16,17 +23,19 @@ impl Default for FreeFlyCamera {
Self {
speed: 0.07,
look_speed: 0.01,
look_with_keys: false
mouse_sensitivity: 0.03,
look_with_keys: false,
}
}
}
impl FreeFlyCamera {
pub fn new(speed: f32, look_speed: f32, look_with_keys: bool) -> Self {
pub fn new(speed: f32, look_speed: f32, mouse_sensitivity: f32, look_with_keys: bool) -> Self {
Self {
speed,
look_speed,
look_with_keys
mouse_sensitivity,
look_with_keys,
}
}
}
@ -37,13 +46,15 @@ impl SimpleSystem for FreeFlyCameraController {
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
let mut camera_rot = Vec3::default();
let keys = world.get_resource::<InputButtons<KeyCode>>()
.map(|r| r.deref().clone());
if keys.is_none() {
return Ok(());
}
let keys = keys.unwrap();
let events = world
.get_resource_mut::<EventQueue>()
.and_then(|q| q.read_events::<MouseMotion>());
let keys = world
.get_resource::<InputButtons<KeyCode>>()
.map(|r| r.deref().clone());
if let Some(keys) = keys.as_ref() {
if keys.is_pressed(KeyCode::Left) {
camera_rot.y += 1.0;
}
@ -67,16 +78,19 @@ impl SimpleSystem for FreeFlyCameraController {
if keys.is_pressed(KeyCode::Q) {
camera_rot.z += 1.0;
}
}
let camera_rot = camera_rot.normalize();
for (cam, fly) in world.query_mut::<(&mut CameraComponent, &mut FreeFlyCamera)>().iter_mut() {
for (cam, fly) in world
.query_mut::<(&mut CameraComponent, &mut FreeFlyCamera)>()
.iter_mut()
{
let forward = cam.transform.forward();
let left = cam.transform.left();
let up = cam.transform.up();
// handle camera movement
if let Some(keys) = keys.as_ref() {
let mut velocity = Vec3::ZERO;
if keys.is_pressed(KeyCode::A) {
velocity -= left;
}
@ -104,10 +118,21 @@ impl SimpleSystem for FreeFlyCameraController {
if velocity != Vec3::ZERO {
cam.transform.translation += velocity.normalize() * fly.speed;
}
}
if !camera_rot.is_nan() {
let look_velocity = camera_rot.normalize() * fly.look_speed;
cam.transform.rotation *= Quat::from_rotation_x(look_velocity.x) * Quat::from_rotation_y(look_velocity.y) * Quat::from_rotation_z(look_velocity.z);
// handle camera rotation
if let Some(mut events) = events.clone() {
while let Some(motion) = events.pop_front() {
camera_rot.x -= motion.delta.y * fly.mouse_sensitivity;
camera_rot.y -= motion.delta.x * fly.mouse_sensitivity;
}
}
if camera_rot != Vec3::ZERO {
let look_velocity = camera_rot * fly.look_speed;
cam.transform.rotation *= Quat::from_rotation_x(look_velocity.x)
* Quat::from_rotation_y(look_velocity.y)
* Quat::from_rotation_z(look_velocity.z);
}
}

View File

@ -23,11 +23,11 @@ pub const INDICES: &[u16] = &[
#[async_std::main]
async fn main() {
let setup_sys = |world: &mut World| -> anyhow::Result<()> {
/* {
{
let mut window_options = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
window_options.cursor_grab = CursorGrabMode::Confined;
window_options.cursor_visible = false;
} */
}
let mut resman = world.get_resource_mut::<ResourceManager>().unwrap();
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();

View File

@ -0,0 +1,28 @@
use edict::Component;
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
#[derive(Clone, Component)]
pub struct FreeFlyCamera {
pub transform: Transform,
pub fov: Angle,
pub mode: CameraProjectionMode,
pub speed: f32,
}
impl Default for FreeFlyCamera {
fn default() -> Self {
Self::new()
}
}
impl FreeFlyCamera {
pub fn new() -> Self {
Self {
transform: Transform::default(),
fov: Angle::Degrees(45.0),
mode: CameraProjectionMode::Perspective,
speed: 1.5,
}
}
}

View File

@ -2,3 +2,4 @@ pub mod mesh;
pub mod model;
pub mod transform;
pub mod camera;
pub mod free_fly_camera;

View File

@ -2,7 +2,7 @@ use std::{sync::Arc, collections::VecDeque};
use async_std::task::block_on;
use tracing::{info, error, Level};
use tracing::{info, error, Level, debug};
use tracing_appender::non_blocking;
use tracing_subscriber::{
layer::SubscriberExt,
@ -12,7 +12,7 @@ use tracing_subscriber::{
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}};
use crate::{render::renderer::{Renderer, BasicRenderer}, input_event::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, plugin::Plugin};
use crate::{render::{renderer::{Renderer, BasicRenderer}, window::WindowOptions}, input_event::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, plugin::Plugin, change_tracker::Ct};
pub struct Controls<'a> {
pub world: &'a mut edict::World,
@ -91,21 +91,6 @@ impl GameLoop {
Some(ControlFlow::Exit)
},
// TODO: Create system for this? or maybe merge into input system, idk
InputEvent::CursorEntered { .. } => {
let state = self.world.with_resource(WindowState::new);
state.is_cursor_inside_window = true;
None
},
InputEvent::CursorLeft { .. } => {
let state = self.world.with_resource(WindowState::new);
state.is_cursor_inside_window = false;
None
}
_ => {
//debug!("Got unhandled input event: \"{:?}\"", event);
@ -122,16 +107,20 @@ impl GameLoop {
*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.get_resource::<WindowState>(), Some(window_state)
if window_state.is_focused && window_state.is_cursor_inside_window); */
let trigger = matches!(self.world.get_resource::<Ct<WindowOptions>>(), Some(window)
if window.focused && window.cursor_inside_window);
if trigger {
let event_queue = self.world.with_resource(Events::<InputEvent>::new);
let mut event_queue = self.world.get_resource_mut::<EventQueue>().unwrap();
let input_event = InputEvent::MouseMotion { device_id, delta, };
event_queue.push_back(input_event);
event_queue.trigger_event(input_event);
}
},
Event::WindowEvent {

View File

@ -258,8 +258,6 @@ impl InputSystem {
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
};
debug!("delta: {:?}", delta);
event_queue.trigger_event(delta);
},
InputEvent::CursorMoved { position, .. } => {

View File

@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{sync::Arc, collections::VecDeque};
use glam::{Vec2, IVec2};
use tracing::{warn, error};
@ -6,7 +6,7 @@ use winit::{window::{Window, Fullscreen}, dpi::{LogicalPosition, LogicalSize, Ph
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
use crate::{plugin::Plugin, change_tracker::Ct};
use crate::{plugin::Plugin, change_tracker::Ct, ecs::EventQueue, input_event::InputEvent};
#[derive(Default, Clone)]
pub enum WindowMode {
@ -171,6 +171,12 @@ pub struct WindowOptions {
/// Change the window level.
/// This is just a hint to the OS, and the system could ignore it.
pub level: WindowLevel,
/// Get/set the window's focused state.
pub focused: bool,
/// Get whether or not the cursor is inside the window.
pub cursor_inside_window: bool,
}
impl Default for WindowOptions {
@ -198,6 +204,8 @@ impl Default for WindowOptions {
title: "Lyra Engine Game".to_string(),
icon: None,
level: WindowLevel::Normal,
focused: false,
cursor_inside_window: false,
}
}
}
@ -252,10 +260,11 @@ fn set_cursor_grab(window: &Window, grab: &mut CursorGrabMode) -> anyhow::Result
Ok(())
}
/// if the window is set to confine the cursor, and the cursor is invisible,
/// set the cursor position to the center of the screen.
/// 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 {
if options.cursor_grab == CursorGrabMode::Confined && !options.cursor_visible
&& options.focused {
let size = window.inner_size();
let middle = PhysicalPosition {
x: size.width / 2,
@ -269,11 +278,14 @@ fn window_updater_system(world: &mut edict::World) -> anyhow::Result<()> {
if let (Some(window), Some(opts)) = (world.get_resource::<Arc<Window>>(), world.get_resource::<Ct<WindowOptions>>()) {
// if the options changed, update the window
if opts.peek_changed() {
drop(opts); // avoid attempting to get a RefMut when we already have a Ref out.
drop(opts); // drop the Ref, we're about to get a RefMut
// now we can get it mutable, this will trigger the ChangeTracker, so it will be reset at the end of this scope.
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
if opts.focused {
window.focus_window();
}
window.set_content_protected(opts.content_protected);
set_cursor_grab(&window, &mut opts.cursor_grab)?;
match window.set_cursor_hittest(opts.cursor_hittest) {
@ -324,6 +336,31 @@ fn window_updater_system(world: &mut edict::World) -> anyhow::Result<()> {
center_mouse(&window, &opts);
} else {
drop(opts); // drop the Ref, we're about to get a RefMut
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
if let Some(event_queue) = world.get_resource_mut::<EventQueue>() {
if let Some(mut events) = event_queue.read_events::<InputEvent>() {
while let Some(ev) = events.pop_front() {
match ev {
InputEvent::CursorEntered { .. } => {
opts.cursor_inside_window = true;
},
InputEvent::CursorLeft { .. } => {
opts.cursor_inside_window = false;
},
_ => {},
}
}
}
}
// update the stored state of the window to match the actual window
opts.focused = window.has_focus();
opts.reset();
center_mouse(&window, &opts);
}
}