From 78f6d3d0ff7f4800defde5e3fec16dbf90049e15 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Thu, 22 Aug 2024 23:37:47 -0300 Subject: [PATCH] Dummy scene with mostly-working camera --- Cargo.toml | 17 ++++++++++ src/camera.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 34 +++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/camera.rs create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ab51bef --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "terroir" +version = "0.1.0" +edition = "2021" + +[dependencies] +bevy = { version = "0.14.1", features = ["dynamic_linking"] } + +[profile.dev] +opt-level = 1 + +[profile.dev.package."*"] +opt-level = 3 + +[profile.release] +codegen-units = 1 +lto = "thin" diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..3489545 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,94 @@ +use { + bevy::{input::mouse::MouseMotion, prelude::*, window::CursorGrabMode}, + std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI, TAU}, +}; + +#[derive(Bundle, Default)] +pub struct PanOrbitCameraBundle { + pub camera: Camera3dBundle, + pub state: PanOrbitState, + pub settings: PanOrbitSettings, +} + +#[derive(Component)] +pub struct PanOrbitState { + pub center: Vec3, + pub radius: f32, + pub pitch: f32, + pub yaw: f32, +} + +impl Default for PanOrbitState { + fn default() -> Self { + PanOrbitState { + center: Vec3::ZERO, + radius: 2.0, + pitch: -FRAC_PI_4, + yaw: 0.0, + } + } +} + +/// The configuration of the pan-orbit controller +#[derive(Component)] +pub struct PanOrbitSettings { + /// World units per pixel of mouse motion + pub pan_sensitivity: f32, + /// Radians per pixel of mouse motion + pub orbit_sensitivity: f32, + /// Exponent per pixel of mouse motion + pub zoom_sensitivity: f32, +} + +impl Default for PanOrbitSettings { + fn default() -> Self { + PanOrbitSettings { + pan_sensitivity: 0.001, // 1000 pixels per world unit + orbit_sensitivity: 0.1f32.to_radians(), // 0.1 degree per pixel + zoom_sensitivity: 0.01, + } + } +} + +pub fn update( + mut evr_motion: EventReader, + mouse: Res>, + mut q_camera: Query<(&PanOrbitSettings, &mut PanOrbitState, &mut Transform)>, + mut q_window: Query<&mut Window>, +) { + let mouse_motion: Vec2 = evr_motion.read().map(|ev| ev.delta).sum(); + + for (settings, mut state, mut transform) in &mut q_camera { + if mouse.any_pressed([MouseButton::Left, MouseButton::Right]) { + q_window.single_mut().cursor.grab_mode = CursorGrabMode::Locked; + } else { + q_window.single_mut().cursor.grab_mode = CursorGrabMode::None; + } + + if mouse.pressed(MouseButton::Right) { + state.pitch = (state.pitch - settings.orbit_sensitivity * mouse_motion.y) + .clamp(-FRAC_PI_2, FRAC_PI_2); + state.yaw += settings.orbit_sensitivity * mouse_motion.x; + if state.yaw > PI { + state.yaw -= TAU; + } + if state.yaw < -PI { + state.yaw += TAU; + } + } + + if mouse.pressed(MouseButton::Left) { + // TODO this needs to take the current rotation into account + state.center += (settings.pan_sensitivity * mouse_motion * -1.0) + .extend(0.0) + .xzy(); + } + + if mouse.pressed(MouseButton::Middle) { + state.radius = (state.radius + settings.zoom_sensitivity * mouse_motion.y).max(0.0); + } + + transform.rotation = Quat::from_euler(EulerRot::YXZ, state.yaw, state.pitch, 0.0); + transform.translation = state.center + transform.back() * state.radius; + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..551884f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,34 @@ +use bevy::prelude::*; + +mod camera; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .add_systems(Update, camera::update) + .run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + commands.spawn(PbrBundle { + mesh: meshes.add(Cuboid::new(1.0, 0.2, 1.0)), + material: materials.add(Color::srgb_u8(124, 144, 255)), + transform: Transform::from_xyz(0.0, -0.2, 0.0), + ..default() + }); + commands.spawn(PointLightBundle { + point_light: PointLight { + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(4.0, 8.0, 4.0), + ..default() + }); + commands.spawn(camera::PanOrbitCameraBundle::default()); +}