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