1#[path = "../helpers/camera_controller.rs"]
4mod camera_controller;
5
6use argh::FromArgs;
7use bevy::{
8 camera::CameraMainTextureUsages,
9 gltf::GltfMaterialName,
10 prelude::*,
11 render::render_resource::TextureUsages,
12 scene::SceneInstanceReady,
13 solari::{
14 pathtracer::{Pathtracer, PathtracingPlugin},
15 prelude::{RaytracingMesh3d, SolariLighting, SolariPlugins},
16 },
17};
18use camera_controller::{CameraController, CameraControllerPlugin};
19use std::f32::consts::PI;
20
21#[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
22use bevy::anti_alias::dlss::{
23 Dlss, DlssProjectId, DlssRayReconstructionFeature, DlssRayReconstructionSupported,
24};
25
26#[derive(FromArgs, Resource, Clone, Copy)]
28struct Args {
29 #[argh(switch)]
31 pathtracer: Option<bool>,
32}
33
34fn main() {
35 let args: Args = argh::from_env();
36
37 let mut app = App::new();
38
39 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
40 app.insert_resource(DlssProjectId(bevy_asset::uuid::uuid!(
41 "5417916c-0291-4e3f-8f65-326c1858ab96" )));
43
44 app.add_plugins((DefaultPlugins, SolariPlugins, CameraControllerPlugin))
45 .insert_resource(args)
46 .add_systems(Startup, setup);
47
48 if args.pathtracer == Some(true) {
49 app.add_plugins(PathtracingPlugin);
50 } else {
51 app.add_systems(Update, (pause_scene, toggle_lights, patrol_path));
52 app.add_systems(PostUpdate, update_text);
53 }
54
55 app.run();
56}
57
58fn setup(
59 mut commands: Commands,
60 asset_server: Res<AssetServer>,
61 args: Res<Args>,
62 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
63 Res<DlssRayReconstructionSupported>,
64 >,
65) {
66 commands
67 .spawn((
68 SceneRoot(
69 asset_server.load(
70 GltfAssetLabel::Scene(0)
71 .from_asset("https://github.com/bevyengine/bevy_asset_files/raw/2a5950295a8b6d9d051d59c0df69e87abcda58c3/pica_pica/mini_diorama_01.glb")
72 ),
73 ),
74 Transform::from_scale(Vec3::splat(10.0)),
75 ))
76 .observe(add_raytracing_meshes_on_scene_load);
77
78 commands
79 .spawn((
80 SceneRoot(asset_server.load(
81 GltfAssetLabel::Scene(0).from_asset("https://github.com/bevyengine/bevy_asset_files/raw/2a5950295a8b6d9d051d59c0df69e87abcda58c3/pica_pica/robot_01.glb")
82 )),
83 Transform::from_scale(Vec3::splat(2.0))
84 .with_translation(Vec3::new(-2.0, 0.05, -2.1))
85 .with_rotation(Quat::from_rotation_y(PI / 2.0)),
86 PatrolPath {
87 path: vec![
88 (Vec3::new(-2.0, 0.05, -2.1), Quat::from_rotation_y(PI / 2.0)),
89 (Vec3::new(2.2, 0.05, -2.1), Quat::from_rotation_y(0.0)),
90 (
91 Vec3::new(2.2, 0.05, 2.1),
92 Quat::from_rotation_y(3.0 * PI / 2.0),
93 ),
94 (Vec3::new(-2.0, 0.05, 2.1), Quat::from_rotation_y(PI)),
95 ],
96 i: 0,
97 },
98 ))
99 .observe(add_raytracing_meshes_on_scene_load);
100
101 commands.spawn((
102 DirectionalLight {
103 illuminance: light_consts::lux::FULL_DAYLIGHT,
104 shadows_enabled: false, ..default()
106 },
107 Transform::from_rotation(Quat::from_xyzw(
108 -0.13334629,
109 -0.86597735,
110 -0.3586996,
111 0.3219264,
112 )),
113 ));
114
115 let mut camera = commands.spawn((
116 Camera3d::default(),
117 Camera {
118 clear_color: ClearColorConfig::Custom(Color::BLACK),
119 ..default()
120 },
121 CameraController {
122 walk_speed: 3.0,
123 run_speed: 10.0,
124 ..Default::default()
125 },
126 Transform::from_translation(Vec3::new(0.219417, 2.5764852, 6.9718704)).with_rotation(
127 Quat::from_xyzw(-0.1466768, 0.013738206, 0.002037309, 0.989087),
128 ),
129 CameraMainTextureUsages::default().with(TextureUsages::STORAGE_BINDING),
131 Msaa::Off,
132 ));
133
134 if args.pathtracer == Some(true) {
135 camera.insert(Pathtracer::default());
136 } else {
137 camera.insert(SolariLighting::default());
138 }
139
140 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
142 if dlss_rr_supported.is_some() {
143 camera.insert(Dlss::<DlssRayReconstructionFeature> {
144 perf_quality_mode: Default::default(),
145 reset: Default::default(),
146 _phantom_data: Default::default(),
147 });
148 }
149
150 commands.spawn((
151 Text::default(),
152 Node {
153 position_type: PositionType::Absolute,
154 bottom: Val::Px(12.0),
155 left: Val::Px(12.0),
156 ..default()
157 },
158 ));
159}
160
161fn add_raytracing_meshes_on_scene_load(
162 scene_ready: On<SceneInstanceReady>,
163 children: Query<&Children>,
164 mesh_query: Query<(
165 &Mesh3d,
166 &MeshMaterial3d<StandardMaterial>,
167 Option<&GltfMaterialName>,
168 )>,
169 mut meshes: ResMut<Assets<Mesh>>,
170 mut materials: ResMut<Assets<StandardMaterial>>,
171 mut commands: Commands,
172 args: Res<Args>,
173) {
174 for descendant in children.iter_descendants(scene_ready.entity) {
175 if let Ok((Mesh3d(mesh_handle), MeshMaterial3d(material_handle), material_name)) =
176 mesh_query.get(descendant)
177 {
178 commands
180 .entity(descendant)
181 .insert(RaytracingMesh3d(mesh_handle.clone()));
182
183 let mesh = meshes.get_mut(mesh_handle).unwrap();
185 if !mesh.contains_attribute(Mesh::ATTRIBUTE_UV_0) {
186 let vertex_count = mesh.count_vertices();
187 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0]; vertex_count]);
188 mesh.insert_attribute(
189 Mesh::ATTRIBUTE_TANGENT,
190 vec![[0.0, 0.0, 0.0, 0.0]; vertex_count],
191 );
192 }
193 if !mesh.contains_attribute(Mesh::ATTRIBUTE_TANGENT) {
194 mesh.generate_tangents().unwrap();
195 }
196 if mesh.contains_attribute(Mesh::ATTRIBUTE_UV_1) {
197 mesh.remove_attribute(Mesh::ATTRIBUTE_UV_1);
198 }
199
200 if args.pathtracer == Some(true) {
202 commands.entity(descendant).remove::<Mesh3d>();
203 }
204
205 if material_name.map(|s| s.0.as_str()) == Some("material") {
207 let material = materials.get_mut(material_handle).unwrap();
208 material.emissive = LinearRgba::BLACK;
209 }
210 if material_name.map(|s| s.0.as_str()) == Some("Lights") {
211 let material = materials.get_mut(material_handle).unwrap();
212 material.emissive =
213 LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
214 material.alpha_mode = AlphaMode::Opaque;
215 material.specular_transmission = 0.0;
216
217 commands.insert_resource(RobotLightMaterial(material_handle.clone()));
218 }
219 if material_name.map(|s| s.0.as_str()) == Some("Glass_Dark_01") {
220 let material = materials.get_mut(material_handle).unwrap();
221 material.alpha_mode = AlphaMode::Opaque;
222 material.specular_transmission = 0.0;
223 }
224 }
225 }
226}
227
228fn pause_scene(mut time: ResMut<Time<Virtual>>, key_input: Res<ButtonInput<KeyCode>>) {
229 if key_input.just_pressed(KeyCode::Space) {
230 if time.is_paused() {
231 time.unpause();
232 } else {
233 time.pause();
234 }
235 }
236}
237
238#[derive(Resource)]
239struct RobotLightMaterial(Handle<StandardMaterial>);
240
241fn toggle_lights(
242 key_input: Res<ButtonInput<KeyCode>>,
243 robot_light_material: Option<Res<RobotLightMaterial>>,
244 mut materials: ResMut<Assets<StandardMaterial>>,
245 directional_light: Query<Entity, With<DirectionalLight>>,
246 mut commands: Commands,
247) {
248 if key_input.just_pressed(KeyCode::Digit1) {
249 if let Ok(directional_light) = directional_light.single() {
250 commands.entity(directional_light).despawn();
251 } else {
252 commands.spawn((
253 DirectionalLight {
254 illuminance: light_consts::lux::FULL_DAYLIGHT,
255 shadows_enabled: false, ..default()
257 },
258 Transform::from_rotation(Quat::from_xyzw(
259 -0.13334629,
260 -0.86597735,
261 -0.3586996,
262 0.3219264,
263 )),
264 ));
265 }
266 }
267
268 if key_input.just_pressed(KeyCode::Digit2)
269 && let Some(robot_light_material) = robot_light_material
270 {
271 let material = materials.get_mut(&robot_light_material.0).unwrap();
272 if material.emissive == LinearRgba::BLACK {
273 material.emissive = LinearRgba::from(Color::srgb(0.941, 0.714, 0.043)) * 1_000_000.0;
274 } else {
275 material.emissive = LinearRgba::BLACK;
276 }
277 }
278}
279
280#[derive(Component)]
281struct PatrolPath {
282 path: Vec<(Vec3, Quat)>,
283 i: usize,
284}
285
286fn patrol_path(mut query: Query<(&mut PatrolPath, &mut Transform)>, time: Res<Time<Virtual>>) {
287 for (mut path, mut transform) in query.iter_mut() {
288 let (mut target_position, mut target_rotation) = path.path[path.i];
289 let mut distance_to_target = transform.translation.distance(target_position);
290 if distance_to_target < 0.01 {
291 transform.translation = target_position;
292 transform.rotation = target_rotation;
293
294 path.i = (path.i + 1) % path.path.len();
295 (target_position, target_rotation) = path.path[path.i];
296 distance_to_target = transform.translation.distance(target_position);
297 }
298
299 let direction = (target_position - transform.translation).normalize();
300 let movement = direction * time.delta_secs();
301
302 if movement.length() > distance_to_target {
303 transform.translation = target_position;
304 transform.rotation = target_rotation;
305 } else {
306 transform.translation += movement;
307 }
308 }
309}
310
311fn update_text(
312 mut text: Single<&mut Text>,
313 robot_light_material: Option<Res<RobotLightMaterial>>,
314 materials: Res<Assets<StandardMaterial>>,
315 directional_light: Query<Entity, With<DirectionalLight>>,
316 time: Res<Time<Virtual>>,
317 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))] dlss_rr_supported: Option<
318 Res<DlssRayReconstructionSupported>,
319 >,
320) {
321 text.0.clear();
322
323 if time.is_paused() {
324 text.0.push_str("(Space): Resume");
325 } else {
326 text.0.push_str("(Space): Pause");
327 }
328
329 if directional_light.single().is_ok() {
330 text.0.push_str("\n(1): Disable directional light");
331 } else {
332 text.0.push_str("\n(1): Enable directional light");
333 }
334
335 match robot_light_material.and_then(|m| materials.get(&m.0)) {
336 Some(robot_light_material) if robot_light_material.emissive != LinearRgba::BLACK => {
337 text.0.push_str("\n(2): Disable robot emissive light");
338 }
339 _ => {
340 text.0.push_str("\n(2): Enable robot emissive light");
341 }
342 }
343
344 #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
345 if dlss_rr_supported.is_some() {
346 text.0
347 .push_str("\nDenoising: DLSS Ray Reconstruction enabled");
348 } else {
349 text.0
350 .push_str("\nDenoising: DLSS Ray Reconstruction not supported");
351 }
352
353 #[cfg(any(not(feature = "dlss"), feature = "force_disable_dlss"))]
354 text.0
355 .push_str("\nDenoising: App not compiled with DLSS support");
356}