diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..e19e67d --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,108 @@ +use bevy::{input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll}, prelude::*}; +use crate::grid::Grid; +use std::{f32::consts::FRAC_PI_2,ops::Range}; + +pub struct GameCameraPlugin; + +impl Plugin for GameCameraPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, scene.spawn()) + .add_systems(Update, orbit); + } +} + +#[derive(Clone,Component)] +pub struct CameraOrbit { + pub distance: f32, + pub pitch_speed: f32, + pub pitch_range: Range, + pub yaw_speed: f32, + pub center_offset: Vec3, + pub vertical_offset_speed: f32, + pub horizontal_offset_speed: f32, +} + +impl Default for CameraOrbit { + fn default() -> Self { + let limit = FRAC_PI_2 - 0.01; + Self { + distance: 20., + yaw_speed: 0.004, + pitch_speed: 0.004, + pitch_range: -limit..limit, + center_offset: Vec3::ZERO, + vertical_offset_speed: 0.05, + horizontal_offset_speed: 0.05, + } + } +} + +pub fn scene() -> impl SceneList { + bsn_list![ + camera(), + sun() + ] +} + +pub fn camera() -> impl Scene { + bsn! { + Camera3d + template( + |ctx| { + let grid = ctx.resource::(); + let center = grid.size.as_vec3() * 0.5; + Ok(Transform::from_xyz( + center.x + grid.size.x as f32 * 1.2, + grid.size.y as f32 * 1.2, + center.z + grid.size.z as f32 * 1.2).looking_at(center, Vec3::Y)) + } + ) + CameraOrbit + } +} + +pub fn sun() -> impl Scene { + bsn!{ + DirectionalLight { + illuminance: 8_000.0, + shadow_maps_enabled: false, + } + template_value(Transform::from_xyz(10.0, 30.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y)) + } +} + +pub fn orbit( + camera: Single<(&mut Transform,&mut CameraOrbit)>, + mouse_buttons: Res>, + mouse_motion: Res, + mouse_scroll: Res, + grid: Res) { + + let (mut transform, mut orbit) = camera.into_inner(); + + if mouse_buttons.pressed(MouseButton::Right) { + let delta = mouse_motion.delta; + let (yaw, pitch, roll) = transform.rotation.to_euler(EulerRot::YXZ); + + let pitch = (pitch - delta.y * orbit.pitch_speed).clamp(orbit.pitch_range.start, orbit.pitch_range.end); + let yaw = yaw - delta.x * orbit.yaw_speed; + + transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll); + } + if mouse_buttons.pressed(MouseButton::Middle) { + let delta = mouse_motion.delta; + + let new_offset = transform.up() * delta.y * orbit.vertical_offset_speed - transform.right() * delta.x * orbit.horizontal_offset_speed; + orbit.center_offset += new_offset; + } + + let center = grid.size.as_vec3() * 0.5; + let min_distance = vec3( + center.x + grid.size.x as f32 * 1.2, + grid.size.y as f32 * 1.2, + center.z + grid.size.z as f32 * 1.2).length(); + + orbit.distance = (orbit.distance-mouse_scroll.delta.y).max(min_distance); + + transform.translation = center + orbit.center_offset -transform.forward() * orbit.distance; +} diff --git a/src/main.rs b/src/main.rs index 45bad8e..b345644 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use crate::camera::GameCameraPlugin; use crate::grid::Grid; use crate::simulation::SimulationPlugin; @@ -7,12 +8,12 @@ use crate::view::ViewPlugin; pub mod simulation; pub mod view; pub mod grid; +pub mod camera; fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(ViewPlugin) - .add_plugins(SimulationPlugin) + .add_plugins((ViewPlugin,SimulationPlugin,GameCameraPlugin)) .insert_resource(Grid::cube(16)) .run(); } diff --git a/src/view/game.rs b/src/view/game.rs index a5fcc43..2b56fd9 100644 --- a/src/view/game.rs +++ b/src/view/game.rs @@ -11,7 +11,7 @@ impl Plugin for GameViewPlugin { app.add_systems(PreStartup, debug_setup_grid) .init_resource::() .init_resource::() - .add_systems(Startup, (setup_view_cubes, scene.spawn())) + .add_systems(Startup, setup_view_cubes) .add_systems(Update, update_cubes); } } @@ -84,38 +84,6 @@ pub fn setup_view_cubes( } } -pub fn scene() -> impl SceneList { - bsn_list![ - camera(), - sun() - ] -} - -pub fn camera() -> impl Scene { - bsn! { - Camera3d - template( - |ctx| { - let grid = ctx.resource::(); - let center = grid.size.as_vec3() * 0.5; - Ok(Transform::from_xyz( - center.x + grid.size.x as f32 * 1.5, - grid.size.y as f32 * 1.5, - center.z + grid.size.z as f32 * 1.5).looking_at(center, Vec3::Y)) - } - ) - } -} - -pub fn sun() -> impl Scene { - bsn!{ - DirectionalLight { - illuminance: 8_000.0, - shadow_maps_enabled: false, - } - template_value(Transform::from_xyz(10.0, 30.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y)) - } -} pub fn cube(position: Vec3,index: usize,mesh: Handle) -> impl Scene {