Skip to content

Commit

Permalink
Merge pull request #64 from johanhelsing/viewport-support
Browse files Browse the repository at this point in the history
Camera viewport support
  • Loading branch information
johanhelsing authored Sep 5, 2024
2 parents ec828f4 + 5ccabe2 commit 1f3289a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 27 deletions.
55 changes: 55 additions & 0 deletions examples/viewport.rs
Original file line number Diff line number Diff line change
@@ -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()
});
}
72 changes: 45 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MouseWheel>,
primary_window: Query<&Window, With<PrimaryWindow>>,
) {
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -256,7 +272,7 @@ fn do_camera_movement(
primary_window: Query<&Window, With<PrimaryWindow>>,
mouse_buttons: Res<ButtonInput<MouseButton>>,
keyboard_buttons: Res<ButtonInput<KeyCode>>,
mut query: Query<(&PanCam, &mut Transform, &OrthographicProjection)>,
mut query: Query<(&PanCam, &Camera, &mut Transform, &OrthographicProjection)>,
mut last_pos: Local<Option<Vec2>>,
time: Res<Time>,
) {
Expand All @@ -273,27 +289,28 @@ fn do_camera_movement(
};
let delta_device_pixels = current_pos - last_pos.unwrap_or(current_pos);

for (cam, mut transform, projection) in &mut query {
if !cam.enabled {
for (pan_cam, camera, mut transform, projection) in &mut query {
if !pan_cam.enabled {
continue;
}

let proj_area_size = projection.area.size();

let mouse_delta = if !cam
let mouse_delta = if !pan_cam
.grab_buttons
.iter()
.any(|btn| mouse_buttons.pressed(*btn) && !mouse_buttons.just_pressed(*btn))
{
Vec2::ZERO
} else {
delta_device_pixels * proj_area_size / window_size
let viewport_size = camera.logical_viewport_size().unwrap_or(window_size);
delta_device_pixels * proj_area_size / viewport_size
};

let direction = cam.move_keys.direction(&keyboard_buttons);
let direction = pan_cam.move_keys.direction(&keyboard_buttons);

let keyboard_delta =
time.delta_seconds() * direction.normalize_or_zero() * cam.speed * projection.scale;
time.delta_seconds() * direction.normalize_or_zero() * pan_cam.speed * projection.scale;
let delta = mouse_delta - keyboard_delta;

if delta == Vec2::ZERO {
Expand All @@ -303,8 +320,9 @@ fn do_camera_movement(
// The proposed new camera position
let proposed_cam_pos = transform.translation.truncate() - delta;

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);
}
*last_pos = Some(current_pos);
}
Expand Down

0 comments on commit 1f3289a

Please sign in to comment.