1use bevy::asset::RenderAssetUsages;
9use bevy::color::{color_difference::EuclideanDistance, palettes::css};
10use bevy::prelude::*;
11use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
12use rand::{Rng, SeedableRng};
13use rand_chacha::ChaCha8Rng;
14
15const IMAGE_WIDTH: u32 = 256;
16const IMAGE_HEIGHT: u32 = 256;
17
18fn main() {
19 App::new()
20 .add_plugins(DefaultPlugins)
21 .insert_resource(Time::<Fixed>::from_hz(1024.0))
26 .add_systems(Startup, setup)
27 .add_systems(FixedUpdate, draw)
28 .run();
29}
30
31#[derive(Resource)]
33struct MyProcGenImage(Handle<Image>);
34
35#[derive(Resource)]
36struct SeededRng(ChaCha8Rng);
37
38fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
39 commands.spawn(Camera2d);
40
41 let mut image = Image::new_fill(
43 Extent3d {
45 width: IMAGE_WIDTH,
46 height: IMAGE_HEIGHT,
47 depth_or_array_layers: 1,
48 },
49 TextureDimension::D2,
50 &(css::BEIGE.to_u8_array()),
52 TextureFormat::Rgba8UnormSrgb,
54 RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
55 );
56
57 for y in 0..IMAGE_HEIGHT {
60 for x in 0..IMAGE_WIDTH {
61 let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
62 let max_radius = IMAGE_HEIGHT.min(IMAGE_WIDTH) as f32 / 2.0;
63 let r = Vec2::new(x as f32, y as f32).distance(center);
64 let a = 1.0 - (r / max_radius).clamp(0.0, 1.0);
65
66 let pixel_bytes = image.pixel_bytes_mut(UVec3::new(x, y, 0)).unwrap();
71 pixel_bytes[3] = (a * u8::MAX as f32) as u8;
73 }
74 }
75
76 let handle = images.add(image);
80
81 commands.spawn(Sprite::from_image(handle.clone()));
83 commands.insert_resource(MyProcGenImage(handle));
84
85 let seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
88 commands.insert_resource(SeededRng(seeded_rng));
89}
90
91fn draw(
93 my_handle: Res<MyProcGenImage>,
94 mut images: ResMut<Assets<Image>>,
95 mut i: Local<u32>,
97 mut draw_color: Local<Color>,
98 mut seeded_rng: ResMut<SeededRng>,
99) {
100 if *i == 0 {
101 *draw_color = Color::linear_rgb(
103 seeded_rng.0.random(),
104 seeded_rng.0.random(),
105 seeded_rng.0.random(),
106 );
107 }
108
109 let image = images.get_mut(&my_handle.0).expect("Image not found");
111
112 let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
115 let max_radius = IMAGE_HEIGHT.min(IMAGE_WIDTH) as f32 / 2.0;
116 let rot_speed = 0.0123;
117 let period = 0.12345;
118
119 let r = ops::sin(*i as f32 * period) * max_radius;
120 let xy = Vec2::from_angle(*i as f32 * rot_speed) * r + center;
121 let (x, y) = (xy.x as u32, xy.y as u32);
122
123 let old_color = image.get_color_at(x, y).unwrap();
125
126 let tolerance = 1.0 / 255.0;
128 if old_color.distance(&draw_color) <= tolerance {
129 *draw_color = Color::linear_rgb(
130 seeded_rng.0.random(),
131 seeded_rng.0.random(),
132 seeded_rng.0.random(),
133 );
134 }
135
136 image
138 .set_color_at(x, y, draw_color.with_alpha(old_color.alpha()))
139 .unwrap();
140
141 *i += 1;
142}