use ecs::component::local::Local; use ecs::phase::UPDATE as UPDATE_PHASE; use ecs::sole::Single; use ecs::system::initializable::Initializable; use ecs::system::Into; use ecs::{Component, Query}; use crate::builder; use crate::camera::{Active as ActiveCamera, Camera}; use crate::delta_time::DeltaTime; use crate::input::keyboard::{Key, KeyState, Keyboard}; use crate::input::mouse::Motion as MouseMotion; use crate::transform::WorldPosition; use crate::vector::{Vec2, Vec3}; builder! { /// A fly camera. #[builder(name = Builder, derives = (Debug))] #[derive(Debug, Component)] #[non_exhaustive] pub struct Fly { pub current_pitch: f64, pub current_yaw: f64, pub speed: f32, } } impl Fly { #[must_use] pub fn builder() -> Builder { Builder::default() } } impl Default for Fly { fn default() -> Self { Self::builder().build() } } impl Default for Builder { fn default() -> Self { Self { current_yaw: -90.0, current_pitch: 0.0, speed: 3.0, } } } /// Fly camera extension. pub struct Extension(pub Options); impl ecs::extension::Extension for Extension { fn collect(self, mut collector: ecs::extension::Collector<'_>) { collector.add_system(*UPDATE_PHASE, update.into_system().initialize((self.0,))); } } #[derive(Debug, Component)] pub struct Options { pub mouse_sensitivity: f32, } fn update( camera_query: Query<(&mut Camera, &mut WorldPosition, &mut Fly, &ActiveCamera)>, keyboard: Single, mouse_motion: Single, delta_time: Single, options: Local, ) { for (mut camera, mut camera_world_pos, mut fly_camera, _) in &camera_query { let delta_time = delta_time.duration; // tracing::info!("Mouse motion: {:?}", mouse_motion.position_delta); if mouse_motion.position_delta != (Vec2 { x: 0.0, y: 0.0 }) { let x_offset = mouse_motion.position_delta.x * f64::from(options.mouse_sensitivity); let y_offset = (-mouse_motion.position_delta.y) * f64::from(options.mouse_sensitivity); fly_camera.current_yaw += x_offset; fly_camera.current_pitch += y_offset; fly_camera.current_pitch = fly_camera.current_pitch.clamp(-89.0, 89.0); } // TODO: This casting to a f32 from a f64 is horrible. fix it #[allow(clippy::cast_possible_truncation)] let direction = Vec3 { x: (fly_camera.current_yaw.to_radians().cos() * fly_camera.current_pitch.to_radians().cos()) as f32, y: fly_camera.current_pitch.to_radians().sin() as f32, z: (fly_camera.current_yaw.to_radians().sin() * fly_camera.current_pitch.to_radians().cos()) as f32, } .normalize(); let cam_right = direction.cross(&Vec3::UP).normalize(); camera.global_up = cam_right.cross(&direction).normalize(); if keyboard.get_key_state(Key::W) == KeyState::Pressed { camera_world_pos.position += direction * fly_camera.speed * delta_time.as_secs_f32(); } if keyboard.get_key_state(Key::S) == KeyState::Pressed { camera_world_pos.position -= direction * fly_camera.speed * delta_time.as_secs_f32(); } if keyboard.get_key_state(Key::A) == KeyState::Pressed { let cam_left = -direction.cross(&Vec3::UP).normalize(); camera_world_pos.position += cam_left * fly_camera.speed * delta_time.as_secs_f32(); } if keyboard.get_key_state(Key::D) == KeyState::Pressed { let cam_right = direction.cross(&Vec3::UP).normalize(); camera_world_pos.position += cam_right * fly_camera.speed * delta_time.as_secs_f32(); } camera.target = camera_world_pos.position + direction; } }