 e57f73207e
			
		
	
	
		e57f73207e
		
			
		
	
	
	
	
		
			
			# Objective - Improve CI when testing rendering by having smarter testbeds ## Solution - CI testing no longer need a config file and will run with a default config if not found - It is now possible to give a name to a screenshot instead of just a frame number - 2d and 3d testbeds are now driven from code - a new system in testbed will watch for state changed - on state changed, trigger a screenshot 100 frames after (so that the scene has time to render) with the name of the scene - when the screenshot is taken (`Captured` component has been removed), switch scene - this means less setup to run a testbed (no need for a config file), screenshots have better names, and it's faster as we don't wait 100 frames for the screenshot to be taken ## Testing - `cargo run --example testbed_2d --features bevy_ci_testing`
		
			
				
	
	
		
			175 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! 2d testbed
 | |
| //!
 | |
| //! You can switch scene by pressing the spacebar
 | |
| 
 | |
| mod helpers;
 | |
| 
 | |
| use bevy::prelude::*;
 | |
| use helpers::Next;
 | |
| 
 | |
| fn main() {
 | |
|     let mut app = App::new();
 | |
|     app.add_plugins((DefaultPlugins,))
 | |
|         .init_state::<Scene>()
 | |
|         .add_systems(OnEnter(Scene::Shapes), shapes::setup)
 | |
|         .add_systems(OnEnter(Scene::Bloom), bloom::setup)
 | |
|         .add_systems(OnEnter(Scene::Text), text::setup)
 | |
|         .add_systems(OnEnter(Scene::Sprite), sprite::setup)
 | |
|         .add_systems(Update, switch_scene);
 | |
| 
 | |
|     #[cfg(feature = "bevy_ci_testing")]
 | |
|     app.add_systems(Update, helpers::switch_scene_in_ci::<Scene>);
 | |
| 
 | |
|     app.run();
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)]
 | |
| #[states(scoped_entities)]
 | |
| enum Scene {
 | |
|     #[default]
 | |
|     Shapes,
 | |
|     Bloom,
 | |
|     Text,
 | |
|     Sprite,
 | |
| }
 | |
| 
 | |
| impl Next for Scene {
 | |
|     fn next(&self) -> Self {
 | |
|         match self {
 | |
|             Scene::Shapes => Scene::Bloom,
 | |
|             Scene::Bloom => Scene::Text,
 | |
|             Scene::Text => Scene::Sprite,
 | |
|             Scene::Sprite => Scene::Shapes,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn switch_scene(
 | |
|     keyboard: Res<ButtonInput<KeyCode>>,
 | |
|     scene: Res<State<Scene>>,
 | |
|     mut next_scene: ResMut<NextState<Scene>>,
 | |
| ) {
 | |
|     if keyboard.just_pressed(KeyCode::Space) {
 | |
|         info!("Switching scene");
 | |
|         next_scene.set(scene.get().next());
 | |
|     }
 | |
| }
 | |
| 
 | |
| mod shapes {
 | |
|     use bevy::prelude::*;
 | |
| 
 | |
|     const X_EXTENT: f32 = 900.;
 | |
| 
 | |
|     pub fn setup(
 | |
|         mut commands: Commands,
 | |
|         mut meshes: ResMut<Assets<Mesh>>,
 | |
|         mut materials: ResMut<Assets<ColorMaterial>>,
 | |
|     ) {
 | |
|         commands.spawn((Camera2d, StateScoped(super::Scene::Shapes)));
 | |
| 
 | |
|         let shapes = [
 | |
|             meshes.add(Circle::new(50.0)),
 | |
|             meshes.add(CircularSector::new(50.0, 1.0)),
 | |
|             meshes.add(CircularSegment::new(50.0, 1.25)),
 | |
|             meshes.add(Ellipse::new(25.0, 50.0)),
 | |
|             meshes.add(Annulus::new(25.0, 50.0)),
 | |
|             meshes.add(Capsule2d::new(25.0, 50.0)),
 | |
|             meshes.add(Rhombus::new(75.0, 100.0)),
 | |
|             meshes.add(Rectangle::new(50.0, 100.0)),
 | |
|             meshes.add(RegularPolygon::new(50.0, 6)),
 | |
|             meshes.add(Triangle2d::new(
 | |
|                 Vec2::Y * 50.0,
 | |
|                 Vec2::new(-50.0, -50.0),
 | |
|                 Vec2::new(50.0, -50.0),
 | |
|             )),
 | |
|         ];
 | |
|         let num_shapes = shapes.len();
 | |
| 
 | |
|         for (i, shape) in shapes.into_iter().enumerate() {
 | |
|             // Distribute colors evenly across the rainbow.
 | |
|             let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7);
 | |
| 
 | |
|             commands.spawn((
 | |
|                 Mesh2d(shape),
 | |
|                 MeshMaterial2d(materials.add(color)),
 | |
|                 Transform::from_xyz(
 | |
|                     // Distribute shapes from -X_EXTENT/2 to +X_EXTENT/2.
 | |
|                     -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT,
 | |
|                     0.0,
 | |
|                     0.0,
 | |
|                 ),
 | |
|                 StateScoped(super::Scene::Shapes),
 | |
|             ));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| mod bloom {
 | |
|     use bevy::{
 | |
|         core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
 | |
|         prelude::*,
 | |
|     };
 | |
| 
 | |
|     pub fn setup(
 | |
|         mut commands: Commands,
 | |
|         mut meshes: ResMut<Assets<Mesh>>,
 | |
|         mut materials: ResMut<Assets<ColorMaterial>>,
 | |
|     ) {
 | |
|         commands.spawn((
 | |
|             Camera2d,
 | |
|             Camera {
 | |
|                 hdr: true,
 | |
|                 ..default()
 | |
|             },
 | |
|             Tonemapping::TonyMcMapface,
 | |
|             Bloom::default(),
 | |
|             StateScoped(super::Scene::Bloom),
 | |
|         ));
 | |
| 
 | |
|         commands.spawn((
 | |
|             Mesh2d(meshes.add(Circle::new(100.))),
 | |
|             MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
 | |
|             Transform::from_translation(Vec3::new(-200., 0., 0.)),
 | |
|             StateScoped(super::Scene::Bloom),
 | |
|         ));
 | |
| 
 | |
|         commands.spawn((
 | |
|             Mesh2d(meshes.add(RegularPolygon::new(100., 6))),
 | |
|             MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))),
 | |
|             Transform::from_translation(Vec3::new(200., 0., 0.)),
 | |
|             StateScoped(super::Scene::Bloom),
 | |
|         ));
 | |
|     }
 | |
| }
 | |
| 
 | |
| mod text {
 | |
|     use bevy::prelude::*;
 | |
| 
 | |
|     pub fn setup(mut commands: Commands) {
 | |
|         let text_font = TextFont {
 | |
|             font_size: 50.0,
 | |
|             ..default()
 | |
|         };
 | |
|         let text_justification = JustifyText::Center;
 | |
|         commands.spawn((Camera2d, StateScoped(super::Scene::Text)));
 | |
|         commands.spawn((
 | |
|             Text2d::new("Hello World"),
 | |
|             text_font,
 | |
|             TextLayout::new_with_justify(text_justification),
 | |
|             StateScoped(super::Scene::Text),
 | |
|         ));
 | |
|     }
 | |
| }
 | |
| 
 | |
| mod sprite {
 | |
|     use bevy::prelude::*;
 | |
| 
 | |
|     pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
 | |
|         commands.spawn((Camera2d, StateScoped(super::Scene::Sprite)));
 | |
|         commands.spawn((
 | |
|             Sprite::from_image(asset_server.load("branding/bevy_bird_dark.png")),
 | |
|             StateScoped(super::Scene::Sprite),
 | |
|         ));
 | |
|     }
 | |
| }
 |