1#![allow(clippy::similar_names)]
10
11use bevy::{
12 camera::{RenderTarget, Viewport},
13 prelude::*,
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 shadow_maps_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 fov = Fixed::default();
79 for (fov, window) in [
80 (fov, WindowRef::Primary),
81 (fov.to_hor(&max), WindowRef::Entity(window2)),
82 (fov.to_upp(&max), WindowRef::Entity(window3)),
83 ] {
84 let mut scope = Scope::default();
85 scope.set_fov(fov);
86 let left = commands
88 .spawn((
89 TrackballController::default(),
90 Camera {
91 order: 0,
92 ..default()
93 },
94 RenderTarget::Window(window),
95 Camera3d::default(),
96 LeftCamera,
97 ))
98 .id();
99 let right = commands
101 .spawn((
102 TrackballController::default(),
103 Camera {
104 order: 1,
105 ..default()
106 },
107 RenderTarget::Window(window),
108 Camera3d::default(),
109 RightCamera,
110 ))
111 .id();
112 commands.entity(left).insert(
114 TrackballCamera::look_at(target, eye, up)
115 .with_scope(scope)
116 .add_controller(right, true),
117 );
118 scope.set_ortho(true);
120 commands.entity(right).insert(
122 TrackballCamera::look_at(target, eye, up)
123 .with_scope(scope)
124 .add_controller(left, true),
125 );
126 }
127}
128
129#[derive(Component)]
130struct LeftCamera;
131
132#[derive(Component)]
133struct RightCamera;
134
135#[allow(clippy::needless_pass_by_value, clippy::type_complexity)]
136fn set_camera_viewports(
137 primary_windows: Query<(Entity, &Window), With<PrimaryWindow>>,
138 windows: Query<(Entity, &Window)>,
139 mut resize_events: MessageReader<WindowResized>,
140 mut left_cameras: Query<(&mut Camera, &RenderTarget), (With<LeftCamera>, Without<RightCamera>)>,
141 mut right_cameras: Query<(&mut Camera, &RenderTarget), With<RightCamera>>,
142) {
143 for resize_event in resize_events.read() {
147 let (resize_id, resize_window) = windows.get(resize_event.window).unwrap();
148 let resolution = &resize_window.resolution;
149 for (mut left_camera, left_target) in &mut left_cameras {
150 if let RenderTarget::Window(window_ref) = left_target {
151 let Some((target_id, _target_window)) = (match window_ref {
152 WindowRef::Primary => primary_windows.single().ok(),
153 WindowRef::Entity(id) => windows.get(*id).ok(),
154 }) else {
155 continue;
156 };
157 if target_id == resize_id {
158 left_camera.viewport = Some(Viewport {
159 physical_position: UVec2::new(0, 0),
160 physical_size: UVec2::new(
161 resolution.physical_width() / 2,
162 resolution.physical_height(),
163 ),
164 ..default()
165 });
166 }
167 }
168 }
169 for (mut right_camera, right_target) in &mut right_cameras {
170 if let RenderTarget::Window(window_ref) = right_target {
171 let Some((target_id, _target_window)) = (match window_ref {
172 WindowRef::Primary => primary_windows.single().ok(),
173 WindowRef::Entity(id) => windows.get(*id).ok(),
174 }) else {
175 continue;
176 };
177 if target_id == resize_id {
178 right_camera.viewport = Some(Viewport {
179 physical_position: UVec2::new(resolution.physical_width() / 2, 0),
180 physical_size: UVec2::new(
181 resolution.physical_width() / 2,
182 resolution.physical_height(),
183 ),
184 ..default()
185 });
186 }
187 }
188 }
189 }
190}