//! Renders a lot of `Text2d`s use std::ops::RangeInclusive; use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, prelude::*, render::view::NoFrustumCulling, text::FontAtlasSets, window::{PresentMode, WindowResolution}, }; use argh::FromArgs; use rand::{ seq::{IteratorRandom, SliceRandom}, Rng, SeedableRng, }; use rand_chacha::ChaCha8Rng; const CAMERA_SPEED: f32 = 1000.0; // Some code points for valid glyphs in `FiraSans-Bold.ttf` const CODE_POINT_RANGES: [RangeInclusive; 5] = [ 0x20..=0x7e, 0xa0..=0x17e, 0x180..=0x2b2, 0x3f0..=0x479, 0x48a..=0x52f, ]; #[derive(FromArgs, Resource)] /// `many_text2d` stress test struct Args { /// whether to use many different glyphs to increase the amount of font atlas textures used. #[argh(switch)] many_glyphs: bool, /// whether to use many different font sizes to increase the amount of font atlas textures used. #[argh(switch)] many_font_sizes: bool, /// whether to force the text to recompute every frame by triggering change detection. #[argh(switch)] recompute: bool, /// whether to disable all frustum culling. #[argh(switch)] no_frustum_culling: bool, /// whether the text should use `JustifyText::Center`. #[argh(switch)] center: bool, } #[derive(Resource)] struct FontHandle(Handle); impl FromWorld for FontHandle { fn from_world(world: &mut World) -> Self { Self(world.load_asset("fonts/FiraSans-Bold.ttf")) } } fn main() { // `from_env` panics on the web #[cfg(not(target_arch = "wasm32"))] let args: Args = argh::from_env(); #[cfg(target_arch = "wasm32")] let args = Args::from_args(&[], &[]).unwrap(); let mut app = App::new(); app.add_plugins(( FrameTimeDiagnosticsPlugin::default(), LogDiagnosticsPlugin::default(), DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { present_mode: PresentMode::AutoNoVsync, resolution: WindowResolution::new(1920.0, 1080.0).with_scale_factor_override(1.0), ..default() }), ..default() }), )) .init_resource::() .add_systems(Startup, setup) .add_systems(Update, (move_camera, print_counts)); if args.recompute { app.add_systems(Update, recompute); } app.insert_resource(args).run(); } #[derive(Deref, DerefMut)] struct PrintingTimer(Timer); impl Default for PrintingTimer { fn default() -> Self { Self(Timer::from_seconds(1.0, TimerMode::Repeating)) } } fn setup(mut commands: Commands, font: Res, args: Res) { warn!(include_str!("warning_string.txt")); let mut rng = ChaCha8Rng::seed_from_u64(42); let tile_size = Vec2::splat(64.0); let map_size = Vec2::splat(640.0); let half_x = (map_size.x / 4.0) as i32; let half_y = (map_size.y / 4.0) as i32; // Spawns the camera commands.spawn(Camera2d); // Builds and spawns the `Text2d`s, distributing them in a way that ensures a // good distribution of on-screen and off-screen entities. let mut text2ds = vec![]; for y in -half_y..half_y { for x in -half_x..half_x { let position = Vec2::new(x as f32, y as f32); let translation = (position * tile_size).extend(rng.r#gen::()); let rotation = Quat::from_rotation_z(rng.r#gen::()); let scale = Vec3::splat(rng.r#gen::() * 2.0); let color = Hsla::hsl(rng.gen_range(0.0..360.0), 0.8, 0.8); text2ds.push(( Text2d(random_text(&mut rng, &args)), random_text_font(&mut rng, &args, font.0.clone()), TextColor(color.into()), TextLayout::new_with_justify(if args.center { JustifyText::Center } else { JustifyText::Left }), Transform { translation, rotation, scale, }, )); } } if args.no_frustum_culling { let bundles = text2ds.into_iter().map(|bundle| (bundle, NoFrustumCulling)); commands.spawn_batch(bundles); } else { commands.spawn_batch(text2ds); } } // System for rotating and translating the camera fn move_camera(time: Res