1mod helpers;
6
7use bevy::prelude::*;
8use helpers::Next;
9
10fn main() {
11 let mut app = App::new();
12 app.add_plugins((DefaultPlugins,))
13 .init_state::<Scene>()
14 .add_systems(OnEnter(Scene::Light), light::setup)
15 .add_systems(OnEnter(Scene::Bloom), bloom::setup)
16 .add_systems(OnEnter(Scene::Gltf), gltf::setup)
17 .add_systems(OnEnter(Scene::Animation), animation::setup)
18 .add_systems(OnEnter(Scene::Gizmos), gizmos::setup)
19 .add_systems(
20 OnEnter(Scene::GltfCoordinateConversion),
21 gltf_coordinate_conversion::setup,
22 )
23 .add_systems(Update, switch_scene)
24 .add_systems(Update, gizmos::draw_gizmos.run_if(in_state(Scene::Gizmos)))
25 .add_systems(
26 Update,
27 gltf_coordinate_conversion::draw_gizmos
28 .run_if(in_state(Scene::GltfCoordinateConversion)),
29 );
30
31 #[cfg(feature = "bevy_ci_testing")]
32 app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
33
34 app.run();
35}
36
37#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
38enum Scene {
39 #[default]
40 Light,
41 Bloom,
42 Gltf,
43 Animation,
44 Gizmos,
45 GltfCoordinateConversion,
46}
47
48impl Next for Scene {
49 fn next(&self) -> Self {
50 match self {
51 Scene::Light => Scene::Bloom,
52 Scene::Bloom => Scene::Gltf,
53 Scene::Gltf => Scene::Animation,
54 Scene::Animation => Scene::Gizmos,
55 Scene::Gizmos => Scene::GltfCoordinateConversion,
56 Scene::GltfCoordinateConversion => Scene::Light,
57 }
58 }
59}
60
61fn switch_scene(
62 keyboard: Res<ButtonInput<KeyCode>>,
63 scene: Res<State<Scene>>,
64 mut next_scene: ResMut<NextState<Scene>>,
65) {
66 if keyboard.just_pressed(KeyCode::Space) {
67 info!("Switching scene");
68 next_scene.set(scene.get().next());
69 }
70}
71
72mod light {
73 use std::f32::consts::PI;
74
75 use bevy::{
76 color::palettes::css::{DEEP_PINK, LIME, RED},
77 prelude::*,
78 };
79
80 const CURRENT_SCENE: super::Scene = super::Scene::Light;
81
82 pub fn setup(
83 mut commands: Commands,
84 mut meshes: ResMut<Assets<Mesh>>,
85 mut materials: ResMut<Assets<StandardMaterial>>,
86 ) {
87 commands.spawn((
88 Mesh3d(meshes.add(Plane3d::default().mesh().size(10.0, 10.0))),
89 MeshMaterial3d(materials.add(StandardMaterial {
90 base_color: Color::WHITE,
91 perceptual_roughness: 1.0,
92 ..default()
93 })),
94 DespawnOnExit(CURRENT_SCENE),
95 ));
96
97 commands.spawn((
98 Mesh3d(meshes.add(Cuboid::default())),
99 MeshMaterial3d(materials.add(StandardMaterial {
100 base_color: DEEP_PINK.into(),
101 ..default()
102 })),
103 Transform::from_xyz(0.0, 1.0, 0.0),
104 DespawnOnExit(CURRENT_SCENE),
105 ));
106
107 commands.spawn((
108 PointLight {
109 intensity: 100_000.0,
110 color: RED.into(),
111 shadows_enabled: true,
112 ..default()
113 },
114 Transform::from_xyz(1.0, 2.0, 0.0),
115 DespawnOnExit(CURRENT_SCENE),
116 ));
117
118 commands.spawn((
119 SpotLight {
120 intensity: 100_000.0,
121 color: LIME.into(),
122 shadows_enabled: true,
123 inner_angle: 0.6,
124 outer_angle: 0.8,
125 ..default()
126 },
127 Transform::from_xyz(-1.0, 2.0, 0.0).looking_at(Vec3::new(-1.0, 0.0, 0.0), Vec3::Z),
128 DespawnOnExit(CURRENT_SCENE),
129 ));
130
131 commands.spawn((
132 DirectionalLight {
133 illuminance: light_consts::lux::OVERCAST_DAY,
134 shadows_enabled: true,
135 ..default()
136 },
137 Transform {
138 translation: Vec3::new(0.0, 2.0, 0.0),
139 rotation: Quat::from_rotation_x(-PI / 4.),
140 ..default()
141 },
142 DespawnOnExit(CURRENT_SCENE),
143 ));
144
145 commands.spawn((
146 Camera3d::default(),
147 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
148 DespawnOnExit(CURRENT_SCENE),
149 ));
150 }
151}
152
153mod bloom {
154 use bevy::{core_pipeline::tonemapping::Tonemapping, post_process::bloom::Bloom, prelude::*};
155
156 const CURRENT_SCENE: super::Scene = super::Scene::Bloom;
157
158 pub fn setup(
159 mut commands: Commands,
160 mut meshes: ResMut<Assets<Mesh>>,
161 mut materials: ResMut<Assets<StandardMaterial>>,
162 ) {
163 commands.spawn((
164 Camera3d::default(),
165 Tonemapping::TonyMcMapface,
166 Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
167 Bloom::NATURAL,
168 DespawnOnExit(CURRENT_SCENE),
169 ));
170
171 let material_emissive1 = materials.add(StandardMaterial {
172 emissive: LinearRgba::rgb(13.99, 5.32, 2.0),
173 ..default()
174 });
175 let material_emissive2 = materials.add(StandardMaterial {
176 emissive: LinearRgba::rgb(2.0, 13.99, 5.32),
177 ..default()
178 });
179
180 let mesh = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap());
181
182 for z in -2..3_i32 {
183 let material = match (z % 2).abs() {
184 0 => material_emissive1.clone(),
185 1 => material_emissive2.clone(),
186 _ => unreachable!(),
187 };
188
189 commands.spawn((
190 Mesh3d(mesh.clone()),
191 MeshMaterial3d(material),
192 Transform::from_xyz(z as f32 * 2.0, 0.0, 0.0),
193 DespawnOnExit(CURRENT_SCENE),
194 ));
195 }
196 }
197}
198
199mod gltf {
200 use bevy::prelude::*;
201
202 const CURRENT_SCENE: super::Scene = super::Scene::Gltf;
203
204 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
205 commands.spawn((
206 Camera3d::default(),
207 Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
208 EnvironmentMapLight {
209 diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
210 specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
211 intensity: 250.0,
212 ..default()
213 },
214 DespawnOnExit(CURRENT_SCENE),
215 ));
216
217 commands.spawn((
218 DirectionalLight {
219 shadows_enabled: true,
220 ..default()
221 },
222 DespawnOnExit(CURRENT_SCENE),
223 ));
224 commands.spawn((
225 SceneRoot(asset_server.load(
226 GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
227 )),
228 DespawnOnExit(CURRENT_SCENE),
229 ));
230 }
231}
232
233mod animation {
234 use std::{f32::consts::PI, time::Duration};
235
236 use bevy::{prelude::*, scene::SceneInstanceReady};
237
238 const CURRENT_SCENE: super::Scene = super::Scene::Animation;
239 const FOX_PATH: &str = "models/animated/Fox.glb";
240
241 #[derive(Resource)]
242 struct Animation {
243 animation: AnimationNodeIndex,
244 graph: Handle<AnimationGraph>,
245 }
246
247 pub fn setup(
248 mut commands: Commands,
249 asset_server: Res<AssetServer>,
250 mut graphs: ResMut<Assets<AnimationGraph>>,
251 ) {
252 let (graph, node) = AnimationGraph::from_clip(
253 asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)),
254 );
255
256 let graph_handle = graphs.add(graph);
257 commands.insert_resource(Animation {
258 animation: node,
259 graph: graph_handle,
260 });
261
262 commands.spawn((
263 Camera3d::default(),
264 Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
265 DespawnOnExit(CURRENT_SCENE),
266 ));
267
268 commands.spawn((
269 Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
270 DirectionalLight {
271 shadows_enabled: true,
272 ..default()
273 },
274 DespawnOnExit(CURRENT_SCENE),
275 ));
276
277 commands
278 .spawn((
279 SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
280 DespawnOnExit(CURRENT_SCENE),
281 ))
282 .observe(pause_animation_frame);
283 }
284
285 fn pause_animation_frame(
286 scene_ready: On<SceneInstanceReady>,
287 children: Query<&Children>,
288 mut commands: Commands,
289 animation: Res<Animation>,
290 mut players: Query<(Entity, &mut AnimationPlayer)>,
291 ) {
292 for child in children.iter_descendants(scene_ready.entity) {
293 if let Ok((entity, mut player)) = players.get_mut(child) {
294 let mut transitions = AnimationTransitions::new();
295 transitions
296 .play(&mut player, animation.animation, Duration::ZERO)
297 .seek_to(0.5)
298 .pause();
299
300 commands
301 .entity(entity)
302 .insert(AnimationGraphHandle(animation.graph.clone()))
303 .insert(transitions);
304 }
305 }
306 }
307}
308
309mod gizmos {
310 use bevy::{color::palettes::css::*, prelude::*};
311
312 pub fn setup(mut commands: Commands) {
313 commands.spawn((
314 Camera3d::default(),
315 Transform::from_xyz(-1.0, 2.5, 6.5).looking_at(Vec3::ZERO, Vec3::Y),
316 DespawnOnExit(super::Scene::Gizmos),
317 ));
318 }
319
320 pub fn draw_gizmos(mut gizmos: Gizmos) {
321 gizmos.cuboid(
322 Transform::from_translation(Vec3::X * -1.75).with_scale(Vec3::splat(1.25)),
323 RED,
324 );
325 gizmos
326 .sphere(Isometry3d::from_translation(Vec3::X * -3.5), 0.75, GREEN)
327 .resolution(30_000 / 3);
328
329 for i in 0..8 {
331 let x = 1.5 * (i % 4) as f32;
332 let y = 1.0 * (0.5 - (i / 4) as f32);
333 let mut grid = gizmos.grid_3d(
334 Isometry3d::from_translation(Vec3::new(x, y, 0.0)),
335 UVec3::new(5, 4, 3),
336 Vec3::splat(0.175),
337 Color::WHITE,
338 );
339 if i & 1 > 0 {
340 grid = grid.outer_edges_x();
341 }
342 if i & 2 > 0 {
343 grid = grid.outer_edges_y();
344 }
345 if i & 4 > 0 {
346 grid.outer_edges_z();
347 }
348 }
349 }
350}
351
352mod gltf_coordinate_conversion {
353 use bevy::{
354 color::palettes::basic::*, gltf::GltfLoaderSettings, prelude::*, scene::SceneInstanceReady,
355 };
356
357 const CURRENT_SCENE: super::Scene = super::Scene::GltfCoordinateConversion;
358
359 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
360 commands.spawn((
361 Camera3d::default(),
362 Transform::from_xyz(-4.0, 4.0, -5.0).looking_at(Vec3::ZERO, Vec3::Y),
363 DespawnOnExit(CURRENT_SCENE),
364 ));
365
366 commands.spawn((
367 DirectionalLight {
368 color: BLUE.into(),
369 ..default()
370 },
371 Transform::IDENTITY.looking_to(Dir3::Z, Dir3::Y),
372 DespawnOnExit(CURRENT_SCENE),
373 ));
374
375 commands.spawn((
376 DirectionalLight {
377 color: RED.into(),
378 ..default()
379 },
380 Transform::IDENTITY.looking_to(Dir3::X, Dir3::Y),
381 DespawnOnExit(CURRENT_SCENE),
382 ));
383
384 commands.spawn((
385 DirectionalLight {
386 color: GREEN.into(),
387 ..default()
388 },
389 Transform::IDENTITY.looking_to(Dir3::NEG_Y, Dir3::X),
390 DespawnOnExit(CURRENT_SCENE),
391 ));
392
393 commands
394 .spawn((
395 SceneRoot(asset_server.load_with_settings(
396 GltfAssetLabel::Scene(0).from_asset("models/Faces/faces.glb"),
397 |s: &mut GltfLoaderSettings| {
398 s.use_model_forward_direction = Some(true);
399 },
400 )),
401 DespawnOnExit(CURRENT_SCENE),
402 ))
403 .observe(show_aabbs);
404 }
405
406 pub fn show_aabbs(
407 scene_ready: On<SceneInstanceReady>,
408 mut commands: Commands,
409 children: Query<&Children>,
410 meshes: Query<(), With<Mesh3d>>,
411 ) {
412 for child in children
413 .iter_descendants(scene_ready.entity)
414 .filter(|&e| meshes.contains(e))
415 {
416 commands.entity(child).insert(ShowAabbGizmo {
417 color: Some(BLACK.into()),
418 });
419 }
420 }
421
422 pub fn draw_gizmos(mut gizmos: Gizmos) {
423 gizmos.axes(Transform::IDENTITY, 1.0);
424 }
425}