panning test
This commit is contained in:
parent
70c7f7228b
commit
6febfecde2
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"./Cargo.toml"
|
||||
]
|
||||
}
|
62
src/main.rs
62
src/main.rs
|
@ -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
146
src/orbit_camera.rs
Normal 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()
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
12
src/vvum.rs
12
src/vvum.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue