1#![allow(clippy::similar_names)]
10
11use bevy::{
12 prelude::*,
13 render::camera::{RenderTarget, Viewport},
14 window::{PrimaryWindow, WindowRef, WindowResized},
15};
16use bevy_trackball::prelude::{Fixed, *};
17
18fn main() {
19 App::new()
20 .add_plugins(DefaultPlugins)
21 .add_plugins(TrackballPlugin)
22 .add_systems(Startup, setup)
23 .add_systems(Update, set_camera_viewports)
24 .run();
25}
26
27fn setup(
29 mut windows: Query<&mut Window>,
30 mut commands: Commands,
31 mut meshes: ResMut<Assets<Mesh>>,
32 mut materials: ResMut<Assets<StandardMaterial>>,
33) {
34 commands.spawn((
36 Mesh3d(meshes.add(Circle::new(4.0))),
37 MeshMaterial3d(materials.add(Color::WHITE)),
38 Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
39 ));
40 commands.spawn((
42 Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
43 MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
44 Transform::from_xyz(0.0, 0.5, 0.0),
45 ));
46 commands.spawn((
48 PointLight {
49 shadows_enabled: true,
50 ..default()
51 },
52 Transform::from_xyz(4.0, 8.0, 4.0),
53 ));
54
55 let mut window1 = windows.single_mut().unwrap();
57 "Fixed Vertical Field of View (Perspective vs Orthographic)".clone_into(&mut window1.title);
58 let res = &window1.resolution;
59 let max = Vec2::new(res.width() * 0.5, res.height()).into();
60 let [target, eye, up] = [Vec3::Y * 0.5, Vec3::new(-2.5, 4.5, 9.0) * 1.2, Vec3::Y];
62 let window2 = commands
64 .spawn(Window {
65 title: "Fixed Horizontal Field of View (Perspective vs Orthographic)".to_owned(),
66 ..default()
67 })
68 .id();
69 let window3 = commands
71 .spawn(Window {
72 title: "Fixed Unit Per Pixels (Perspective vs Orthographic)".to_owned(),
73 ..default()
74 })
75 .id();
76
77 let mut order = 0;
79 let fov = Fixed::default();
80 for (fov, window) in [
81 (fov, WindowRef::Primary),
82 (fov.to_hor(&max), WindowRef::Entity(window2)),
83 (fov.to_upp(&max), WindowRef::Entity(window3)),
84 ] {
85 let mut scope = Scope::default();
86 scope.set_fov(fov);
87 let left = commands
89 .spawn((
90 TrackballController::default(),
91 Camera {
92 target: RenderTarget::Window(window),
93 order,
96 ..default()
97 },
98 Camera3d::default(),
99 LeftCamera,
100 ))
101 .id();
102 order += 1;
103 let right = commands
105 .spawn((
106 TrackballController::default(),
107 Camera {
108 target: RenderTarget::Window(window),
109 order,
112 clear_color: ClearColorConfig::None,
115 ..default()
116 },
117 Camera3d::default(),
118 RightCamera,
119 ))
120 .id();
121 order += 1;
122 commands.entity(left).insert(
124 TrackballCamera::look_at(target, eye, up)
125 .with_scope(scope)
126 .add_controller(right, true),
127 );
128 scope.set_ortho(true);
130 commands.entity(right).insert(
132 TrackballCamera::look_at(target, eye, up)
133 .with_scope(scope)
134 .add_controller(left, true),
135 );
136 }
137}
138
139#[derive(Component)]
140struct LeftCamera;
141
142#[derive(Component)]
143struct RightCamera;
144
145#[allow(clippy::needless_pass_by_value)]
146fn set_camera_viewports(
147 primary_windows: Query<(Entity, &Window), With<PrimaryWindow>>,
148 windows: Query<(Entity, &Window)>,
149 mut resize_events: EventReader<WindowResized>,
150 mut left_cameras: Query<&mut Camera, (With<LeftCamera>, Without<RightCamera>)>,
151 mut right_cameras: Query<&mut Camera, With<RightCamera>>,
152) {
153 for resize_event in resize_events.read() {
157 let (resize_id, resize_window) = windows.get(resize_event.window).unwrap();
158 let resolution = &resize_window.resolution;
159 for mut left_camera in &mut left_cameras {
160 if let RenderTarget::Window(window_ref) = left_camera.target {
161 let Some((target_id, _target_window)) = (match window_ref {
162 WindowRef::Primary => primary_windows.single().ok(),
163 WindowRef::Entity(id) => windows.get(id).ok(),
164 }) else {
165 continue;
166 };
167 if target_id == resize_id {
168 left_camera.viewport = Some(Viewport {
169 physical_position: UVec2::new(0, 0),
170 physical_size: UVec2::new(
171 resolution.physical_width() / 2,
172 resolution.physical_height(),
173 ),
174 ..default()
175 });
176 }
177 }
178 }
179 for mut right_camera in &mut right_cameras {
180 if let RenderTarget::Window(window_ref) = right_camera.target {
181 let Some((target_id, _target_window)) = (match window_ref {
182 WindowRef::Primary => primary_windows.single().ok(),
183 WindowRef::Entity(id) => windows.get(id).ok(),
184 }) else {
185 continue;
186 };
187 if target_id == resize_id {
188 right_camera.viewport = Some(Viewport {
189 physical_position: UVec2::new(resolution.physical_width() / 2, 0),
190 physical_size: UVec2::new(
191 resolution.physical_width() / 2,
192 resolution.physical_height(),
193 ),
194 ..default()
195 });
196 }
197 }
198 }
199 }
200}