panning test

This commit is contained in:
Lillian Vixe 2024-02-18 04:23:38 -08:00
parent 70c7f7228b
commit 6febfecde2
4 changed files with 167 additions and 58 deletions

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"rust-analyzer.linkedProjects": [
"./Cargo.toml"
]
}

View file

@ -1,60 +1,20 @@
use bevy::app::*;
use bevy::ecs::component::*;
use bevy::time::*;
use bevy::ecs::query::*;
use bevy::ecs::system::*;
use bevy::prelude::*;
use bevy::window::WindowResolution;
use bevy::DefaultPlugins;
mod orbit_camera;
mod vvum;
use vvum::mesh_plugin;
#[derive(Component)]
struct Person;
#[derive(Component)]
struct Name(String);
fn add_people(mut commands: Commands) {
commands.spawn((Person, Name("Elaina Proctor".to_string())));
commands.spawn((Person, Name("Renzo Hume".to_string())));
commands.spawn((Person, Name("Zayna Nieves".to_string())));
}
pub struct HelloPlugin;
impl Plugin for HelloPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(GreetTimer(Timer::from_seconds(2.0, TimerMode::Repeating)))
.add_systems(Startup, add_people)
.add_systems(Update, greet_people);
}
}
#[derive(Resource)]
struct GreetTimer(Timer);
fn greet_people(
time: Res<Time>,
mut timer: ResMut<GreetTimer>,
query: Query<&Name, With<Person>>
) {
// update our timer with the time elapsed since the last update
// if that caused the timer to finish, we say hello to everyone
if timer.0.tick(time.delta()).just_finished() {
for name in &query {
println!("hello {}!", name.0);
}
}
}
fn main() {
App::new()
.add_plugins(
(DefaultPlugins, HelloPlugin)
)
.add_plugins(
vvum::mesh_plugin::MeshPlugin
)
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(1152., 864.).with_scale_factor_override(1.0),
..default()
}),
..default()
}))
.add_plugins(mesh_plugin::PluginInitializer)
.run();
}

146
src/orbit_camera.rs Normal file
View file

@ -0,0 +1,146 @@
pub mod orbit_camera {
use bevy::input::mouse::MouseMotion;
use bevy::input::mouse::MouseWheel;
use bevy::prelude::*;
use bevy::window::*;
use bevy::*;
/// Tags an entity as capable of panning and orbiting.
#[derive(Component)]
struct PanOrbitCamera {
/// The "focus point" to orbit around. It is automatically updated when panning the camera
pub focus: Vec3,
pub radius: f32,
pub upside_down: bool,
}
impl Default for PanOrbitCamera {
fn default() -> Self {
PanOrbitCamera {
focus: Vec3::ZERO,
radius: 5.0,
upside_down: false,
}
}
}
/// Pan the camera with middle mouse click, zoom with scroll wheel, orbit with right mouse click.
pub fn pan_orbit_camera(
window_query: &Query<&Window, With<PrimaryWindow>>,
mut ev_motion: EventReader<MouseMotion>,
mut ev_scroll: EventReader<MouseWheel>,
input_mouse: Res<Input<MouseButton>>,
mut query: Query<(&mut PanOrbitCamera, &mut Transform, &Projection)>,
) {
// change input mapping for orbit and panning here
let orbit_button = MouseButton::Right;
let pan_button = MouseButton::Middle;
let mut pan = Vec2::ZERO;
let mut rotation_move = Vec2::ZERO;
let mut scroll = 0.0;
let mut orbit_button_changed = false;
if input_mouse.pressed(orbit_button) {
for ev in ev_motion.iter() {
rotation_move += ev.delta;
}
} else if input_mouse.pressed(pan_button) {
// Pan only if we're not rotating at the moment
for ev in ev_motion.iter() {
pan += ev.delta;
}
}
for ev in ev_scroll.iter() {
scroll += ev.y;
}
if input_mouse.just_released(orbit_button) || input_mouse.just_pressed(orbit_button) {
orbit_button_changed = true;
}
for (mut pan_orbit, mut transform, projection) in query.iter_mut() {
if orbit_button_changed {
// only check for upside down when orbiting started or ended this frame
// if the camera is "upside" down, panning horizontally would be inverted, so invert the input to make it correct
let up = transform.rotation * Vec3::Y;
pan_orbit.upside_down = up.y <= 0.0;
}
let mut any = false;
if rotation_move.length_squared() > 0.0 {
any = true;
let window = get_primary_window_size(window_query);
let delta_x = {
let delta = rotation_move.x / window.x * std::f32::consts::PI * 2.0;
if pan_orbit.upside_down {
-delta
} else {
delta
}
};
let delta_y = rotation_move.y / window.y * std::f32::consts::PI;
let yaw = Quat::from_rotation_y(-delta_x);
let pitch = Quat::from_rotation_x(-delta_y);
transform.rotation = yaw * transform.rotation; // rotate around global y axis
transform.rotation = transform.rotation * pitch; // rotate around local x axis
} else if pan.length_squared() > 0.0 {
any = true;
// make panning distance independent of resolution and FOV,
let window = get_primary_window_size(window_query);
if let Projection::Perspective(projection) = projection {
pan *= Vec2::new(projection.fov * projection.aspect_ratio, projection.fov)
/ window;
}
// translate by local axes
let right = transform.rotation * Vec3::X * -pan.x;
let up = transform.rotation * Vec3::Y * pan.y;
// make panning proportional to distance away from focus point
let translation = (right + up) * pan_orbit.radius;
pan_orbit.focus += translation;
} else if scroll.abs() > 0.0 {
any = true;
pan_orbit.radius -= scroll * pan_orbit.radius * 0.2;
// dont allow zoom to reach zero or you get stuck
pan_orbit.radius = f32::max(pan_orbit.radius, 0.05);
}
if any {
// emulating parent/child to make the yaw/y-axis rotation behave like a turntable
// parent = x and y rotation
// child = z-offset
let rot_matrix = Mat3::from_quat(transform.rotation);
transform.translation =
pan_orbit.focus + rot_matrix.mul_vec3(Vec3::new(0.0, 0.0, pan_orbit.radius));
}
}
// consume any remaining events, so they don't pile up if we don't need them
// (and also to avoid Bevy warning us about not checking events every frame update)
ev_motion.clear();
}
fn get_primary_window_size(window_query: &Query<&Window, With<PrimaryWindow>>) -> Vec2 {
let Ok(window) = window_query.get_single() else {
return Vec2::ZERO;
};
let window = Vec2::new(window.width() as f32, window.height() as f32);
window
}
/// Spawn a camera like this
pub fn spawn_camera(mut commands: Commands) {
let translation = Vec3::new(-2.0, 2.5, 5.0);
let radius = translation.length();
commands.spawn((
Camera3dBundle {
transform: Transform::from_translation(translation).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
},
PanOrbitCamera {
radius,
..Default::default()
},
));
}
}

View file

@ -7,11 +7,13 @@ pub mod mesh_plugin {
},
};
use crate::orbit_camera::orbit_camera::spawn_camera;
#[derive(Event)]
pub struct SendMeshEvent {}
pub struct MeshPlugin;
impl Plugin for MeshPlugin {
pub struct PluginInitializer;
impl Plugin for PluginInitializer {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup);
}
@ -230,11 +232,6 @@ pub mod mesh_plugin {
let camera_and_light_transform =
Transform::from_xyz(1.8, 1.8, 1.8).looking_at(Vec3::ZERO, Vec3::Y);
commands.spawn(Camera3dBundle {
transform: camera_and_light_transform,
..default()
});
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 1000.0,
@ -244,5 +241,6 @@ pub mod mesh_plugin {
transform: camera_and_light_transform,
..default()
});
spawn_camera(commands);
}
}