-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
projection_zoom.rs
166 lines (153 loc) · 6.74 KB
/
projection_zoom.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Shows how to zoom orthographic and perspective projection cameras.
use std::{f32::consts::PI, ops::Range};
use bevy::{input::mouse::AccumulatedMouseScroll, prelude::*, render::camera::ScalingMode};
#[derive(Debug, Resource)]
struct CameraSettings {
/// The height of the viewport in world units when the orthographic camera's scale is 1
pub orthographic_viewport_height: f32,
/// Clamp the orthographic camera's scale to this range
pub orthographic_zoom_range: Range<f32>,
/// Multiply mouse wheel inputs by this factor when using the orthographic camera
pub orthographic_zoom_speed: f32,
/// Clamp perspective camera's field of view to this range
pub perspective_zoom_range: Range<f32>,
/// Multiply mouse wheel inputs by this factor when using the perspective camera
pub perspective_zoom_speed: f32,
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(CameraSettings {
orthographic_viewport_height: 5.,
// In orthographic projections, we specify camera scale relative to a default value of 1,
// in which one unit in world space corresponds to one pixel.
orthographic_zoom_range: 0.1..10.0,
// This value was hand-tuned to ensure that zooming in and out feels smooth but not slow.
orthographic_zoom_speed: 0.2,
// Perspective projections use field of view, expressed in radians. We would
// normally not set it to more than π, which represents a 180° FOV.
perspective_zoom_range: (PI / 5.)..(PI - 0.2),
// Changes in FOV are much more noticeable due to its limited range in radians
perspective_zoom_speed: 0.05,
})
.add_systems(Startup, (setup, instructions))
.add_systems(Update, (switch_projection, zoom))
.run();
}
/// Set up a simple 3D scene
fn setup(
asset_server: Res<AssetServer>,
camera_settings: Res<CameraSettings>,
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Name::new("Camera"),
Camera3d::default(),
Projection::from(OrthographicProjection {
// We can set the scaling mode to FixedVertical to keep the viewport height constant as its aspect ratio changes.
// The viewport height is the height of the camera's view in world units when the scale is 1.
scaling_mode: ScalingMode::FixedVertical {
viewport_height: camera_settings.orthographic_viewport_height,
},
// This is the default value for scale for orthographic projections.
// To zoom in and out, change this value, rather than `ScalingMode` or the camera's position.
scale: 1.,
..OrthographicProjection::default_3d()
}),
Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn((
Name::new("Plane"),
Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.3, 0.5, 0.3),
// Turning off culling keeps the plane visible when viewed from beneath.
cull_mode: None,
..default()
})),
));
commands.spawn((
Name::new("Fox"),
SceneRoot(
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
),
// Note: the scale adjustment is purely an accident of our fox model, which renders
// HUGE unless mitigated!
Transform::from_translation(Vec3::splat(0.0)).with_scale(Vec3::splat(0.025)),
));
commands.spawn((
Name::new("Light"),
PointLight::default(),
Transform::from_xyz(3.0, 8.0, 5.0),
));
}
fn instructions(mut commands: Commands) {
commands.spawn((
Name::new("Instructions"),
Text::new(
"Scroll mouse wheel to zoom in/out\n\
Space: switch between orthographic and perspective projections",
),
Node {
position_type: PositionType::Absolute,
top: Val::Px(12.),
left: Val::Px(12.),
..default()
},
));
}
fn switch_projection(
mut camera: Single<&mut Projection, With<Camera>>,
camera_settings: Res<CameraSettings>,
keyboard_input: Res<ButtonInput<KeyCode>>,
) {
if keyboard_input.just_pressed(KeyCode::Space) {
// Switch projection type
**camera = match **camera {
Projection::Orthographic(_) => Projection::Perspective(PerspectiveProjection {
fov: camera_settings.perspective_zoom_range.start,
..default()
}),
Projection::Perspective(_) => Projection::Orthographic(OrthographicProjection {
scaling_mode: ScalingMode::FixedVertical {
viewport_height: camera_settings.orthographic_viewport_height,
},
..OrthographicProjection::default_3d()
}),
}
}
}
fn zoom(
camera: Single<&mut Projection, With<Camera>>,
camera_settings: Res<CameraSettings>,
mouse_wheel_input: Res<AccumulatedMouseScroll>,
) {
// Usually, you won't need to handle both types of projection,
// but doing so makes for a more complete example.
match *camera.into_inner() {
Projection::Orthographic(ref mut orthographic) => {
// We want scrolling up to zoom in, decreasing the scale, so we negate the delta.
let delta_zoom = -mouse_wheel_input.delta.y * camera_settings.orthographic_zoom_speed;
// When changing scales, logarithmic changes are more intuitive.
// To get this effect, we add 1 to the delta, so that a delta of 0
// results in no multiplicative effect, positive values result in a multiplicative increase,
// and negative values result in multiplicative decreases.
let multiplicative_zoom = 1. + delta_zoom;
orthographic.scale = (orthographic.scale * multiplicative_zoom).clamp(
camera_settings.orthographic_zoom_range.start,
camera_settings.orthographic_zoom_range.end,
);
}
Projection::Perspective(ref mut perspective) => {
// We want scrolling up to zoom in, decreasing the scale, so we negate the delta.
let delta_zoom = -mouse_wheel_input.delta.y * camera_settings.perspective_zoom_speed;
// Adjust the field of view, but keep it within our stated range.
perspective.fov = (perspective.fov + delta_zoom).clamp(
camera_settings.perspective_zoom_range.start,
camera_settings.perspective_zoom_range.end,
);
}
}
}