use std::{f32::consts, mem}; use bevy::{ math::vec3, prelude::*, window::{CursorGrabMode, PrimaryWindow}, }; use crate::input::{Action, CameraMotion, InputAction, Lifecycle}; pub struct OrbitCamPlugin; #[derive(SystemSet, Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct CameraMotionSystem; impl Plugin for OrbitCamPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, spawn_camera); app.add_systems( Update, ( camera_control_kinematic.in_set(CameraMotionSystem), update_velocity .in_set(CameraMotionSystem) .after(camera_control_kinematic), camera_control_physical .in_set(CameraMotionSystem) .after(update_velocity), ), ); } } // TODO: These don't really belong in this module #[derive(Component)] pub struct Velocity(pub Vec3); #[derive(Component)] pub struct TargetVelocity(pub Vec3); #[derive(Component)] pub struct Interp(pub f32); #[derive(Component)] pub struct OrbitCam { pub focus: Vec3, pub radius: f32, pub upside_down: bool, } impl Default for OrbitCam { fn default() -> Self { Self { focus: Vec3::ZERO, radius: 10.0, upside_down: false, } } } fn spawn_camera(mut cmd: Commands) { let focus = vec3(0., 1.5, 0.); let camera_xform = Transform::from_xyz(-2.5, 4.5, 5.0).looking_at(focus, Vec3::Y); cmd.spawn(( Camera3dBundle { transform: camera_xform, camera: Camera { hdr: true, ..default() }, ..default() }, OrbitCam { focus, radius: Vec3::distance(camera_xform.translation, focus), ..default() }, Velocity(Vec3::ZERO), TargetVelocity(Vec3::ZERO), Interp(1.), )); } fn camera_control_kinematic( mut q_window: Query<&mut Window, With>, mut input: EventReader, mut q_camera: Query<( &mut OrbitCam, &mut TargetVelocity, &mut Interp, &mut Transform, &Projection, )>, ) { if input.is_empty() { return; } let (mut orbit_cam, mut target_vel, mut t, mut xform, proj) = q_camera.single_mut(); let mut window = q_window.single_mut(); let mut any_ongoing = false; let mut any_started = false; let mut any_stopped = false; for action in input.iter() { #[allow(irrefutable_let_patterns)] // Will eventually be more variants if let InputAction(lifecycle, Action::Camera(motion)) = action { use CameraMotion::*; // Ignore translation actions for the sake of pointer capture if mem::discriminant(motion) != mem::discriminant(&Translate(Vec3::ZERO)) { match lifecycle { Lifecycle::Start => any_started = true, Lifecycle::Ongoing => any_ongoing = true, Lifecycle::Stop => any_stopped = true, } } match motion { Translate(delta) => do_translate(*delta, &mut target_vel, &mut t), Pan(delta) => do_pan(proj, &window, *delta, &mut xform, &mut orbit_cam), Zoom(delta) => do_zoom(&window, *delta, &mut xform, &mut orbit_cam), Orbit(delta) => do_orbit(&window, *delta, *lifecycle, &mut xform, &mut orbit_cam), Rotate(delta) => do_rotate(&window, *delta, &mut xform, &mut orbit_cam), } } } if any_started { capture_pointer(&mut window); } else if any_stopped && !any_ongoing { release_pointer(&mut window); } } // TODO: This doesn't really belong in this module fn update_velocity( time: Res