1mod helpers;
6
7use argh::FromArgs;
8use bevy::prelude::*;
9use helpers::Next;
10
11#[derive(FromArgs)]
12pub struct Args {
14 #[argh(positional)]
15 scene: Option<Scene>,
16}
17
18fn main() {
19 #[cfg(not(target_arch = "wasm32"))]
20 let args: Args = argh::from_env();
21 #[cfg(target_arch = "wasm32")]
22 let args: Args = Args::from_args(&[], &[]).unwrap();
23
24 let mut app = App::new();
25 app.add_plugins((DefaultPlugins,))
26 .add_systems(OnEnter(Scene::Light), light::setup)
27 .add_systems(OnEnter(Scene::Bloom), bloom::setup)
28 .add_systems(OnEnter(Scene::Gltf), gltf::setup)
29 .add_systems(OnEnter(Scene::Animation), animation::setup)
30 .add_systems(OnEnter(Scene::Gizmos), gizmos::setup)
31 .add_systems(
32 OnEnter(Scene::GltfCoordinateConversion),
33 gltf_coordinate_conversion::setup,
34 )
35 .add_systems(
36 OnEnter(Scene::WhiteFurnaceSolidColorLight),
37 white_furnace_solid_color_light::setup,
38 )
39 .add_systems(
40 OnEnter(Scene::WhiteFurnaceEnvironmentMapLight),
41 white_furnace_environment_map_light::setup,
42 )
43 .add_systems(OnEnter(Scene::RenderLayers), render_layers::setup)
44 .add_systems(Update, switch_scene)
45 .add_systems(Update, gizmos::draw_gizmos.run_if(in_state(Scene::Gizmos)))
46 .add_systems(
47 Update,
48 gltf_coordinate_conversion::draw_gizmos
49 .run_if(in_state(Scene::GltfCoordinateConversion)),
50 );
51
52 match args.scene {
53 None => app.init_state::<Scene>(),
54 Some(scene) => app.insert_state(scene),
55 };
56
57 #[cfg(feature = "bevy_ci_testing")]
58 app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
59
60 app.run();
61}
62
63#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, States, Default)]
64enum Scene {
65 #[default]
66 Light,
67 Bloom,
68 Gltf,
69 Animation,
70 Gizmos,
71 GltfCoordinateConversion,
72 WhiteFurnaceSolidColorLight,
73 WhiteFurnaceEnvironmentMapLight,
74 RenderLayers,
75}
76
77impl Scene {
78 const ALL_ORDERED: &'static [Scene] = &[
79 Scene::Light,
80 Scene::Bloom,
81 Scene::Gltf,
82 Scene::Animation,
83 Scene::Gizmos,
84 Scene::GltfCoordinateConversion,
85 Scene::WhiteFurnaceSolidColorLight,
86 Scene::WhiteFurnaceEnvironmentMapLight,
87 Scene::RenderLayers,
88 ];
89}
90
91impl std::str::FromStr for Scene {
92 type Err = String;
93
94 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
95 let mut isit = Self::default();
96 while s.to_lowercase() != format!("{isit:?}").to_lowercase() {
97 isit = isit.next();
98 if isit == Self::default() {
99 return Err(format!("Invalid Scene name: {s}"));
100 }
101 }
102 Ok(isit)
103 }
104}
105
106impl Next for Scene {
107 fn next(&self) -> Self {
108 Scene::ALL_ORDERED[(Scene::ALL_ORDERED
109 .iter()
110 .position(|scene| scene == self)
111 .unwrap()
112 + 1)
113 % Scene::ALL_ORDERED.len()]
114 }
115}
116
117fn switch_scene(
118 keyboard: Res<ButtonInput<KeyCode>>,
119 scene: Res<State<Scene>>,
120 mut next_scene: ResMut<NextState<Scene>>,
121) {
122 if keyboard.just_pressed(KeyCode::Space) {
123 info!("Switching scene");
124 next_scene.set(scene.get().next());
125 }
126}
127
128mod light {
129 use std::f32::consts::PI;
130
131 use bevy::{
132 color::palettes::css::{DEEP_PINK, LIME, RED},
133 prelude::*,
134 };
135
136 const CURRENT_SCENE: super::Scene = super::Scene::Light;
137
138 pub fn setup(
139 mut commands: Commands,
140 mut meshes: ResMut<Assets<Mesh>>,
141 mut materials: ResMut<Assets<StandardMaterial>>,
142 ) {
143 commands.spawn((
144 Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
145 MeshMaterial3d(materials.add(StandardMaterial {
146 base_color: Color::WHITE,
147 perceptual_roughness: 1.0,
148 ..default()
149 })),
150 DespawnOnExit(CURRENT_SCENE),
151 ));
152
153 commands.spawn((
154 Mesh3d(meshes.add(Cuboid::default())),
155 MeshMaterial3d(materials.add(StandardMaterial {
156 base_color: DEEP_PINK.into(),
157 ..default()
158 })),
159 Transform::from_xyz(0.0, 1.0, 0.0),
160 DespawnOnExit(CURRENT_SCENE),
161 ));
162
163 commands.spawn((
164 PointLight {
165 intensity: 100_000.0,
166 color: RED.into(),
167 shadow_maps_enabled: true,
168 ..default()
169 },
170 Transform::from_xyz(1.0, 2.0, 0.0),
171 DespawnOnExit(CURRENT_SCENE),
172 ));
173
174 commands.spawn((
175 SpotLight {
176 intensity: 100_000.0,
177 color: LIME.into(),
178 shadow_maps_enabled: true,
179 inner_angle: 0.6,
180 outer_angle: 0.8,
181 ..default()
182 },
183 Transform::from_xyz(-1.0, 2.0, 0.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Z),
184 DespawnOnExit(CURRENT_SCENE),
185 ));
186
187 commands.spawn((
188 DirectionalLight {
189 illuminance: light_consts::lux::OVERCAST_DAY,
190 shadow_maps_enabled: true,
191 ..default()
192 },
193 Transform {
194 translation: Vec3::new(0.0, 2.0, 0.0),
195 rotation: Quat::from_rotation_x(-PI / 4.),
196 ..default()
197 },
198 DespawnOnExit(CURRENT_SCENE),
199 ));
200
201 commands.spawn((
202 RectLight {
203 color: Color::srgb(0.5, 0.7, 1.0),
204 intensity: 100_000.0,
205 width: 1.5,
206 height: 4.0,
207 range: 20.0,
208 },
209 Transform::from_xyz(1.0, 2.0, -2.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Y),
210 DespawnOnExit(CURRENT_SCENE),
211 ));
212
213 commands.spawn((
214 Camera3d::default(),
215 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
216 DespawnOnExit(CURRENT_SCENE),
217 ));
218 }
219}
220
221mod bloom {
222 use bevy::{core_pipeline::tonemapping::Tonemapping, post_process::bloom::Bloom, prelude::*};
223
224 const CURRENT_SCENE: super::Scene = super::Scene::Bloom;
225
226 pub fn setup(
227 mut commands: Commands,
228 mut meshes: ResMut<Assets<Mesh>>,
229 mut materials: ResMut<Assets<StandardMaterial>>,
230 ) {
231 commands.spawn((
232 Camera3d::default(),
233 Tonemapping::TonyMcMapface,
234 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
235 Bloom::NATURAL,
236 DespawnOnExit(CURRENT_SCENE),
237 ));
238
239 let material_emissive1 = materials.add(StandardMaterial {
240 emissive: LinearRgba::rgb(13.99, 5.32, 2.0),
241 ..default()
242 });
243 let material_emissive2 = materials.add(StandardMaterial {
244 emissive: LinearRgba::rgb(2.0, 13.99, 5.32),
245 ..default()
246 });
247
248 let mesh = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap());
249
250 for z in -2..3_i32 {
251 let material = match (z % 2).abs() {
252 0 => material_emissive1.clone(),
253 1 => material_emissive2.clone(),
254 _ => unreachable!(),
255 };
256
257 commands.spawn((
258 Mesh3d(mesh.clone()),
259 MeshMaterial3d(material),
260 Transform::from_xyz(z as f32 * 2.0, 0.0, 0.0),
261 DespawnOnExit(CURRENT_SCENE),
262 ));
263 }
264 }
265}
266
267mod gltf {
268 use bevy::prelude::*;
269
270 const CURRENT_SCENE: super::Scene = super::Scene::Gltf;
271
272 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
273 commands.spawn((
274 Camera3d::default(),
275 Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
276 EnvironmentMapLight {
277 diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
278 specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
279 intensity: 250.0,
280 ..default()
281 },
282 DespawnOnExit(CURRENT_SCENE),
283 ));
284
285 commands.spawn((
286 DirectionalLight {
287 shadow_maps_enabled: true,
288 ..default()
289 },
290 DespawnOnExit(CURRENT_SCENE),
291 ));
292 commands.spawn((
293 WorldAssetRoot(asset_server.load(
294 GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
295 )),
296 DespawnOnExit(CURRENT_SCENE),
297 ));
298 }
299}
300
301mod animation {
302 use std::{f32::consts::PI, time::Duration};
303
304 use bevy::{prelude::*, world_serialization::WorldInstanceReady};
305
306 const CURRENT_SCENE: super::Scene = super::Scene::Animation;
307 const FOX_PATH: &str = "models/animated/Fox.glb";
308
309 #[derive(Resource)]
310 struct Animation {
311 animation: AnimationNodeIndex,
312 graph: Handle<AnimationGraph>,
313 }
314
315 pub fn setup(
316 mut commands: Commands,
317 asset_server: Res<AssetServer>,
318 mut graphs: ResMut<Assets<AnimationGraph>>,
319 ) {
320 let (graph, node) = AnimationGraph::from_clip(
321 asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)),
322 );
323
324 let graph_handle = graphs.add(graph);
325 commands.insert_resource(Animation {
326 animation: node,
327 graph: graph_handle,
328 });
329
330 commands.spawn((
331 Camera3d::default(),
332 Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
333 DespawnOnExit(CURRENT_SCENE),
334 ));
335
336 commands.spawn((
337 Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
338 DirectionalLight {
339 shadow_maps_enabled: true,
340 ..default()
341 },
342 DespawnOnExit(CURRENT_SCENE),
343 ));
344
345 commands
346 .spawn((
347 WorldAssetRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
348 DespawnOnExit(CURRENT_SCENE),
349 ))
350 .observe(pause_animation_frame);
351 }
352
353 fn pause_animation_frame(
354 scene_ready: On<WorldInstanceReady>,
355 children: Query<&Children>,
356 mut commands: Commands,
357 animation: Res<Animation>,
358 mut players: Query<(Entity, &mut AnimationPlayer)>,
359 ) {
360 for child in children.iter_descendants(scene_ready.entity) {
361 if let Ok((entity, mut player)) = players.get_mut(child) {
362 let mut transitions = AnimationTransitions::new();
363 transitions
364 .play(&mut player, animation.animation, Duration::ZERO)
365 .seek_to(0.5)
366 .pause();
367
368 commands
369 .entity(entity)
370 .insert(AnimationGraphHandle(animation.graph.clone()))
371 .insert(transitions);
372 }
373 }
374 }
375}
376
377mod gizmos {
378 use bevy::{color::palettes::css::*, prelude::*};
379
380 pub fn setup(mut commands: Commands) {
381 commands.spawn((
382 Camera3d::default(),
383 Transform::from_xyz(-1.0, 2.5, 6.5).looking_at(Vec3::ZERO, Vec3::Y),
384 DespawnOnExit(super::Scene::Gizmos),
385 ));
386 }
387
388 pub fn draw_gizmos(mut gizmos: Gizmos) {
389 gizmos.cube(
390 Transform::from_translation(Vec3::X * -1.75).with_scale(Vec3::splat(1.25)),
391 RED,
392 );
393 gizmos
394 .sphere(Isometry3d::from_translation(Vec3::X * -3.5), 0.75, GREEN)
395 .resolution(30_000 / 3);
396
397 gizmos.text(
398 Isometry3d::from_translation(Vec3::Y * 1.5),
399 "text gizmo",
400 0.3,
401 Vec2 { x: 0., y: 0. },
402 Color::WHITE,
403 );
404
405 for i in 0..8 {
407 let x = 1.5 * (i % 4) as f32;
408 let y = 1.0 * (0.5 - (i / 4) as f32);
409 let mut grid = gizmos.grid_3d(
410 Isometry3d::from_translation(Vec3::new(x, y, 0.0)),
411 UVec3::new(5, 4, 3),
412 Vec3::splat(0.175),
413 Color::WHITE,
414 );
415 if i & 1 > 0 {
416 grid = grid.outer_edges_x();
417 }
418 if i & 2 > 0 {
419 grid = grid.outer_edges_y();
420 }
421 if i & 4 > 0 {
422 grid.outer_edges_z();
423 }
424 }
425 }
426}
427
428mod gltf_coordinate_conversion {
429 use bevy::{
430 color::palettes::basic::*,
431 gltf::{convert_coordinates::GltfConvertCoordinates, GltfLoaderSettings},
432 prelude::*,
433 world_serialization::WorldInstanceReady,
434 };
435
436 const CURRENT_SCENE: super::Scene = super::Scene::GltfCoordinateConversion;
437
438 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
439 commands.spawn((
440 Camera3d::default(),
441 Transform::from_xyz(-4.0, 4.0, -5.0).looking_at(Vec3::ZERO, Vec3::Y),
442 DespawnOnExit(CURRENT_SCENE),
443 ));
444
445 commands.spawn((
446 DirectionalLight {
447 color: BLUE.into(),
448 ..default()
449 },
450 Transform::IDENTITY.looking_to(Dir3::Z, Dir3::Y),
451 DespawnOnExit(CURRENT_SCENE),
452 ));
453
454 commands.spawn((
455 DirectionalLight {
456 color: RED.into(),
457 ..default()
458 },
459 Transform::IDENTITY.looking_to(Dir3::X, Dir3::Y),
460 DespawnOnExit(CURRENT_SCENE),
461 ));
462
463 commands.spawn((
464 DirectionalLight {
465 color: GREEN.into(),
466 ..default()
467 },
468 Transform::IDENTITY.looking_to(Dir3::NEG_Y, Dir3::X),
469 DespawnOnExit(CURRENT_SCENE),
470 ));
471
472 commands
473 .spawn((
474 WorldAssetRoot(
475 asset_server
476 .load_builder()
477 .with_settings(|s: &mut GltfLoaderSettings| {
478 s.convert_coordinates = Some(GltfConvertCoordinates {
479 rotate_scene_entity: true,
480 rotate_meshes: true,
481 });
482 })
483 .load(GltfAssetLabel::Scene(0).from_asset("models/Faces/faces.glb")),
484 ),
485 DespawnOnExit(CURRENT_SCENE),
486 ))
487 .observe(show_aabbs);
488 }
489
490 pub fn show_aabbs(
491 scene_ready: On<WorldInstanceReady>,
492 mut commands: Commands,
493 children: Query<&Children>,
494 meshes: Query<(), With<Mesh3d>>,
495 ) {
496 for child in children
497 .iter_descendants(scene_ready.entity)
498 .filter(|&e| meshes.contains(e))
499 {
500 commands.entity(child).insert(ShowAabbGizmo {
501 color: Some(BLACK.into()),
502 });
503 }
504 }
505
506 pub fn draw_gizmos(mut gizmos: Gizmos) {
507 gizmos.axes(Transform::IDENTITY, 1.0);
508 }
509}
510
511mod white_furnace_solid_color_light {
512 use bevy::{
513 asset::RenderAssetUsages,
514 camera::{Hdr, ScalingMode},
515 core_pipeline::tonemapping::{DebandDither, Tonemapping},
516 light::Skybox,
517 prelude::*,
518 render::render_resource::{
519 Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor, TextureViewDimension,
520 },
521 };
522
523 const CURRENT_SCENE: super::Scene = super::Scene::WhiteFurnaceSolidColorLight;
524
525 fn create_white_cubemap(size: u32) -> Image {
527 const WHITE_F16: [u8; 2] = [0, 60];
529 const WHITE_PIXEL: [u8; 8] = [
530 WHITE_F16[0],
531 WHITE_F16[1], WHITE_F16[0],
533 WHITE_F16[1], WHITE_F16[0],
535 WHITE_F16[1], WHITE_F16[0],
537 WHITE_F16[1], ];
539
540 let pixel_data: Vec<u8> = (0..6 * size * size).flat_map(|_| WHITE_PIXEL).collect();
541
542 Image {
543 texture_view_descriptor: Some(TextureViewDescriptor {
544 dimension: Some(TextureViewDimension::Cube),
545 ..Default::default()
546 }),
547 ..Image::new(
548 Extent3d {
549 width: size,
550 height: size,
551 depth_or_array_layers: 6,
552 },
553 TextureDimension::D2,
554 pixel_data,
555 TextureFormat::Rgba16Float,
556 RenderAssetUsages::RENDER_WORLD,
557 )
558 }
559 }
560
561 pub fn setup(
562 mut commands: Commands,
563 mut meshes: ResMut<Assets<Mesh>>,
564 mut materials: ResMut<Assets<StandardMaterial>>,
565 mut images: ResMut<Assets<Image>>,
566 ) {
567 let sphere_mesh = meshes.add(Sphere::new(0.45));
568
569 commands.insert_resource(GlobalAmbientLight::NONE);
571
572 for y in -2..=2 {
574 for x in -5..=5 {
575 let x01 = (x + 5) as f32 / 10.0;
576 let y01 = (y + 2) as f32 / 4.0;
577 commands.spawn((
579 Mesh3d(sphere_mesh.clone()),
580 MeshMaterial3d(materials.add(StandardMaterial {
581 base_color: LinearRgba::WHITE.into(),
582 metallic: y01,
584 perceptual_roughness: x01,
585 ..default()
586 })),
587 Transform::from_xyz(x as f32, y as f32 + 0.5, 0.0),
588 DespawnOnExit(CURRENT_SCENE),
589 ));
590 }
591 }
592
593 let white_cubemap = create_white_cubemap(256);
595 let white_cubemap_handle = images.add(white_cubemap);
596
597 let mut solid_color_light = EnvironmentMapLight::solid_color(&mut images, Color::WHITE);
598 solid_color_light.intensity = 500.0;
599
600 commands.spawn((
602 Camera3d::default(),
603 Hdr,
604 Tonemapping::None,
605 DebandDither::Disabled,
606 Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::default(), Vec3::Y),
607 Projection::from(OrthographicProjection {
608 scale: 0.01,
609 scaling_mode: ScalingMode::WindowSize,
610 ..OrthographicProjection::default_3d()
611 }),
612 Skybox {
613 image: Some(white_cubemap_handle),
614 brightness: 500.0,
616 ..default()
617 },
618 solid_color_light,
619 DespawnOnExit(CURRENT_SCENE),
620 ));
621 }
622}
623
624mod white_furnace_environment_map_light {
625 use bevy::{
626 asset::RenderAssetUsages,
627 camera::{Hdr, ScalingMode},
628 core_pipeline::tonemapping::{DebandDither, Tonemapping},
629 light::Skybox,
630 prelude::*,
631 render::render_resource::{
632 Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor, TextureViewDimension,
633 },
634 };
635
636 const CURRENT_SCENE: super::Scene = super::Scene::WhiteFurnaceEnvironmentMapLight;
637
638 fn create_white_cubemap(size: u32) -> Image {
640 const WHITE_F16: [u8; 2] = [0, 60];
642 const WHITE_PIXEL: [u8; 8] = [
643 WHITE_F16[0],
644 WHITE_F16[1], WHITE_F16[0],
646 WHITE_F16[1], WHITE_F16[0],
648 WHITE_F16[1], WHITE_F16[0],
650 WHITE_F16[1], ];
652
653 let pixel_data: Vec<u8> = (0..6 * size * size).flat_map(|_| WHITE_PIXEL).collect();
654
655 Image {
656 texture_view_descriptor: Some(TextureViewDescriptor {
657 dimension: Some(TextureViewDimension::Cube),
658 ..Default::default()
659 }),
660 ..Image::new(
661 Extent3d {
662 width: size,
663 height: size,
664 depth_or_array_layers: 6,
665 },
666 TextureDimension::D2,
667 pixel_data,
668 TextureFormat::Rgba16Float,
669 RenderAssetUsages::RENDER_WORLD,
670 )
671 }
672 }
673
674 pub fn setup(
675 mut commands: Commands,
676 mut meshes: ResMut<Assets<Mesh>>,
677 mut materials: ResMut<Assets<StandardMaterial>>,
678 mut images: ResMut<Assets<Image>>,
679 ) {
680 let sphere_mesh = meshes.add(Sphere::new(0.45));
681
682 commands.insert_resource(GlobalAmbientLight::NONE);
684
685 for y in -2..=2 {
687 for x in -5..=5 {
688 let x01 = (x + 5) as f32 / 10.0;
689 let y01 = (y + 2) as f32 / 4.0;
690 commands.spawn((
692 Mesh3d(sphere_mesh.clone()),
693 MeshMaterial3d(materials.add(StandardMaterial {
694 base_color: LinearRgba::WHITE.into(),
695 metallic: y01,
697 perceptual_roughness: x01,
698 ..default()
699 })),
700 Transform::from_xyz(x as f32, y as f32 + 0.5, 0.0),
701 DespawnOnExit(CURRENT_SCENE),
702 ));
703 }
704 }
705
706 let white_cubemap = create_white_cubemap(256);
708 let white_cubemap_handle = images.add(white_cubemap);
709
710 let generated_light = GeneratedEnvironmentMapLight {
711 environment_map: white_cubemap_handle.clone(),
712 intensity: 500.0,
713 ..default()
714 };
715
716 commands.spawn((
718 Camera3d::default(),
719 Hdr,
720 Tonemapping::None,
721 DebandDither::Disabled,
722 Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::default(), Vec3::Y),
723 Projection::from(OrthographicProjection {
724 scale: 0.01,
725 scaling_mode: ScalingMode::WindowSize,
726 ..OrthographicProjection::default_3d()
727 }),
728 Skybox {
729 image: Some(white_cubemap_handle),
730 brightness: 500.0,
732 ..default()
733 },
734 generated_light,
735 DespawnOnExit(CURRENT_SCENE),
736 ));
737 }
738}
739
740mod render_layers {
741 const CURRENT_SCENE: super::Scene = super::Scene::RenderLayers;
742
743 use bevy::{
744 camera::{visibility::RenderLayers, Viewport},
745 prelude::*,
746 window::PrimaryWindow,
747 };
748
749 pub fn setup(
750 mut commands: Commands,
751 mut meshes: ResMut<Assets<Mesh>>,
752 mut materials: ResMut<Assets<StandardMaterial>>,
753 window: Single<&Window, With<PrimaryWindow>>,
754 ) {
755 commands.spawn((
757 Mesh3d(meshes.add(Circle::new(4.0))),
758 MeshMaterial3d(materials.add(Color::WHITE)),
759 Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
760 RenderLayers::layer(0).with(1).with(2),
761 DespawnOnExit(CURRENT_SCENE),
762 ));
763
764 commands.spawn((
766 Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
767 MeshMaterial3d(materials.add(Color::srgb(1.0, 0.0, 0.0))),
768 Transform::from_xyz(-1.5, 0.5, 0.0),
769 DespawnOnExit(CURRENT_SCENE),
771 ));
772 commands.spawn((
773 Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
774 MeshMaterial3d(materials.add(Color::srgb(0.0, 1.0, 0.0))),
775 Transform::from_xyz(0.0, 0.5, 0.0),
776 RenderLayers::layer(1),
777 DespawnOnExit(CURRENT_SCENE),
778 ));
779 commands.spawn((
780 Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
781 MeshMaterial3d(materials.add(Color::srgb(0.0, 0.0, 1.0))),
782 Transform::from_xyz(1.5, 0.5, 0.0),
783 RenderLayers::layer(2),
784 DespawnOnExit(CURRENT_SCENE),
785 ));
786
787 commands.spawn((
789 PointLight {
790 shadow_maps_enabled: true,
791 ..default()
792 },
793 Transform::from_xyz(4.0, 8.0, 4.0),
794 DespawnOnExit(CURRENT_SCENE),
795 ));
796
797 let window_half_size = window.physical_size() / 2;
798
799 for index in 0..4 {
802 let viewport_pos = UVec2::new((index % 2) as u32, (index / 2) as u32);
803 let mut entity_cmds = commands.spawn((
804 Camera3d::default(),
805 Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
806 Camera {
807 order: index as isize,
809 viewport: Some(Viewport {
810 physical_position: viewport_pos * window_half_size,
811 physical_size: window_half_size,
812 ..default()
813 }),
814 ..default()
815 },
816 DespawnOnExit(CURRENT_SCENE),
817 ));
818 match index {
819 0 => {}
820 1 => {
821 entity_cmds.insert(RenderLayers::layer(1));
822 }
823 2 => {
824 entity_cmds.insert(RenderLayers::layer(2));
825 }
826 3 => {
827 entity_cmds.insert(RenderLayers::layer(0).with(1).with(2));
828 }
829 _ => warn!("Unexpected index {index}"),
830 }
831 }
832 }
833}