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