diff --git a/examples/viewport.rs b/examples/viewport.rs new file mode 100644 index 0000000..67bc206 --- /dev/null +++ b/examples/viewport.rs @@ -0,0 +1,55 @@ +use bevy::{prelude::*, render::camera::Viewport}; +use bevy_pancam::{PanCam, PanCamPlugin}; +use rand::prelude::random; + +fn main() { + App::new() + .add_plugins((DefaultPlugins, PanCamPlugin::default())) + .add_systems(Startup, setup) + .run(); +} + +fn setup(mut commands: Commands) { + commands.spawn(( + Camera2dBundle { + camera: Camera { + viewport: Some(Viewport { + physical_position: UVec2::new(100, 200), + physical_size: UVec2::new(600, 400), + depth: 0.0..1.0, + }), + ..Camera2dBundle::default().camera + }, + ..default() + }, + PanCam { + min_x: -500., + min_y: -500., + max_x: 500., + max_y: 500., + ..default() + }, + )); + + // background + commands.spawn(SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.3, 0.3, 0.3), + custom_size: Some(Vec2::new(1000., 1000.)), + ..default() + }, + transform: Transform::from_xyz(0., 0., 0.), + ..default() + }); + + // red square + commands.spawn(SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.8, 0.3, 0.3), + custom_size: Some(Vec2::new(100., 100.)), + ..default() + }, + transform: Transform::from_xyz(0., 0., 1.), + ..default() + }); +} diff --git a/src/lib.rs b/src/lib.rs index 84f327b..6fd4af4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,12 @@ fn check_egui_wants_focus( } fn do_camera_zoom( - mut query: Query<(&PanCam, &mut OrthographicProjection, &mut Transform)>, + mut query: Query<( + &PanCam, + &Camera, + &mut OrthographicProjection, + &mut Transform, + )>, scroll_events: EventReader, primary_window: Query<&Window, With>, ) { @@ -152,47 +157,58 @@ fn do_camera_zoom( let Ok(window) = primary_window.get_single() else { return; }; - let window_size = window.size(); - - let cursor_normalized_screen_pos = window - .cursor_position() - .map(|cursor_pos| (cursor_pos / window_size) * 2. - Vec2::ONE) - .map(|p| vec2(p.x, -p.y)); - for (cam, mut proj, mut transform) in &mut query { - if !cam.enabled { + for (pan_cam, camera, mut proj, mut transform) in &mut query { + if !pan_cam.enabled { continue; } + let view_size = camera.logical_viewport_size().unwrap_or(window.size()); + let old_scale = proj.scale; proj.scale *= 1. - scroll_offset * ZOOM_SENSITIVITY; constrain_proj_scale( &mut proj, - cam.rect().size(), - &cam.scale_range(), - window_size, + pan_cam.rect().size(), + &pan_cam.scale_range(), + view_size, ); + let cursor_normalized_viewport_pos = window + .cursor_position() + .map(|cursor_pos| { + let view_pos = camera + .logical_viewport_rect() + .map(|v| v.min) + .unwrap_or(Vec2::ZERO); + + ((cursor_pos - view_pos) / view_size) * 2. - Vec2::ONE + }) + .map(|p| vec2(p.x, -p.y)); + // Move the camera position to normalize the projection window - let (Some(cursor_normalized_screen_pos), true) = - (cursor_normalized_screen_pos, cam.zoom_to_cursor) + let (Some(cursor_normalized_view_pos), true) = + (cursor_normalized_viewport_pos, pan_cam.zoom_to_cursor) else { continue; }; let proj_size = proj.area.max / old_scale; + let cursor_world_pos = - transform.translation.truncate() + cursor_normalized_screen_pos * proj_size * old_scale; + transform.translation.truncate() + cursor_normalized_view_pos * proj_size * old_scale; + let proposed_cam_pos = - cursor_world_pos - cursor_normalized_screen_pos * proj_size * proj.scale; + cursor_world_pos - cursor_normalized_view_pos * proj_size * proj.scale; // As we zoom out, we don't want the viewport to move beyond the provided // boundary. If the most recent change to the camera zoom would move cause // parts of the window beyond the boundary to be shown, we need to change the // camera position to keep the viewport within bounds. - transform.translation = clamp_to_safe_zone(proposed_cam_pos, cam.aabb(), proj.area.size()) - .extend(transform.translation.z); + transform.translation = + clamp_to_safe_zone(proposed_cam_pos, pan_cam.aabb(), proj.area.size()) + .extend(transform.translation.z); } } @@ -256,7 +272,7 @@ fn do_camera_movement( primary_window: Query<&Window, With>, mouse_buttons: Res>, keyboard_buttons: Res>, - mut query: Query<(&PanCam, &mut Transform, &OrthographicProjection)>, + mut query: Query<(&PanCam, &Camera, &mut Transform, &OrthographicProjection)>, mut last_pos: Local>, time: Res