1use bevy::{asset::LoadedFolder, image::ImageSampler, prelude::*};
11
12fn main() {
13 App::new()
14 .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .init_state::<AppState>()
16 .add_systems(OnEnter(AppState::Setup), load_textures)
17 .add_systems(Update, check_textures.run_if(in_state(AppState::Setup)))
18 .add_systems(OnEnter(AppState::Finished), setup)
19 .run();
20}
21
22#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, States)]
23enum AppState {
24 #[default]
25 Setup,
26 Finished,
27}
28
29#[derive(Resource, Default)]
30struct RpgSpriteFolder(Handle<LoadedFolder>);
31
32fn load_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
33 commands.insert_resource(RpgSpriteFolder(asset_server.load_folder("textures/rpg")));
35}
36
37fn check_textures(
38 mut next_state: ResMut<NextState<AppState>>,
39 rpg_sprite_folder: Res<RpgSpriteFolder>,
40 mut events: EventReader<AssetEvent<LoadedFolder>>,
41) {
42 for event in events.read() {
44 if event.is_loaded_with_dependencies(&rpg_sprite_folder.0) {
45 next_state.set(AppState::Finished);
46 }
47 }
48}
49
50fn setup(
51 mut commands: Commands,
52 rpg_sprite_handles: Res<RpgSpriteFolder>,
53 asset_server: Res<AssetServer>,
54 mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
55 loaded_folders: Res<Assets<LoadedFolder>>,
56 mut textures: ResMut<Assets<Image>>,
57) {
58 let loaded_folder = loaded_folders.get(&rpg_sprite_handles.0).unwrap();
59
60 let (texture_atlas_linear, linear_sources, linear_texture) = create_texture_atlas(
63 loaded_folder,
64 None,
65 Some(ImageSampler::linear()),
66 &mut textures,
67 );
68 let atlas_linear_handle = texture_atlases.add(texture_atlas_linear);
69
70 let (texture_atlas_nearest, nearest_sources, nearest_texture) = create_texture_atlas(
71 loaded_folder,
72 None,
73 Some(ImageSampler::nearest()),
74 &mut textures,
75 );
76 let atlas_nearest_handle = texture_atlases.add(texture_atlas_nearest);
77
78 let (texture_atlas_linear_padded, linear_padded_sources, linear_padded_texture) =
79 create_texture_atlas(
80 loaded_folder,
81 Some(UVec2::new(6, 6)),
82 Some(ImageSampler::linear()),
83 &mut textures,
84 );
85 let atlas_linear_padded_handle = texture_atlases.add(texture_atlas_linear_padded.clone());
86
87 let (texture_atlas_nearest_padded, nearest_padded_sources, nearest_padded_texture) =
88 create_texture_atlas(
89 loaded_folder,
90 Some(UVec2::new(6, 6)),
91 Some(ImageSampler::nearest()),
92 &mut textures,
93 );
94 let atlas_nearest_padded_handle = texture_atlases.add(texture_atlas_nearest_padded);
95
96 commands.spawn(Camera2d);
98
99 commands.spawn((
103 Sprite::from_image(linear_texture.clone()),
104 Transform {
105 translation: Vec3::new(-250.0, -130.0, 0.0),
106 scale: Vec3::splat(0.8),
107 ..default()
108 },
109 ));
110
111 commands.spawn((
113 Sprite::from_image(linear_padded_texture.clone()),
114 Transform {
115 translation: Vec3::new(250.0, -130.0, 0.0),
116 scale: Vec3::splat(0.8),
117 ..default()
118 },
119 ));
120
121 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
122
123 let text_style: TextFont = TextFont {
125 font: font.clone(),
126 font_size: 42.0,
127 ..default()
128 };
129
130 create_label(
134 &mut commands,
135 (-250.0, 330.0, 0.0),
136 "No padding",
137 text_style.clone(),
138 );
139
140 create_label(&mut commands, (250.0, 330.0, 0.0), "Padding", text_style);
142
143 let vendor_handle: Handle<Image> = asset_server
145 .get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
146 .unwrap();
147
148 let configurations: [(
150 &str,
151 Handle<TextureAtlasLayout>,
152 TextureAtlasSources,
153 Handle<Image>,
154 f32,
155 ); 4] = [
156 (
157 "Linear",
158 atlas_linear_handle,
159 linear_sources,
160 linear_texture,
161 -350.0,
162 ),
163 (
164 "Nearest",
165 atlas_nearest_handle,
166 nearest_sources,
167 nearest_texture,
168 -150.0,
169 ),
170 (
171 "Linear",
172 atlas_linear_padded_handle,
173 linear_padded_sources,
174 linear_padded_texture,
175 150.0,
176 ),
177 (
178 "Nearest",
179 atlas_nearest_padded_handle,
180 nearest_padded_sources,
181 nearest_padded_texture,
182 350.0,
183 ),
184 ];
185
186 let sampling_label_style = TextFont {
188 font,
189 font_size: 25.0,
190 ..default()
191 };
192
193 let base_y = 170.0; for (sampling, atlas_handle, atlas_sources, atlas_texture, x) in configurations {
196 create_sprite_from_atlas(
198 &mut commands,
199 (x, base_y, 0.0),
200 atlas_texture,
201 atlas_sources,
202 atlas_handle,
203 &vendor_handle,
204 );
205
206 create_label(
208 &mut commands,
209 (x, base_y + 110.0, 0.0), sampling,
211 sampling_label_style.clone(),
212 );
213 }
214}
215
216fn create_texture_atlas(
219 folder: &LoadedFolder,
220 padding: Option<UVec2>,
221 sampling: Option<ImageSampler>,
222 textures: &mut ResMut<Assets<Image>>,
223) -> (TextureAtlasLayout, TextureAtlasSources, Handle<Image>) {
224 let mut texture_atlas_builder = TextureAtlasBuilder::default();
226 texture_atlas_builder.padding(padding.unwrap_or_default());
227 for handle in folder.handles.iter() {
228 let id = handle.id().typed_unchecked::<Image>();
229 let Some(texture) = textures.get(id) else {
230 warn!(
231 "{:?} did not resolve to an `Image` asset.",
232 handle.path().unwrap()
233 );
234 continue;
235 };
236
237 texture_atlas_builder.add_texture(Some(id), texture);
238 }
239
240 let (texture_atlas_layout, texture_atlas_sources, texture) =
241 texture_atlas_builder.build().unwrap();
242 let texture = textures.add(texture);
243
244 let image = textures.get_mut(&texture).unwrap();
246 image.sampler = sampling.unwrap_or_default();
247
248 (texture_atlas_layout, texture_atlas_sources, texture)
249}
250
251fn create_sprite_from_atlas(
253 commands: &mut Commands,
254 translation: (f32, f32, f32),
255 atlas_texture: Handle<Image>,
256 atlas_sources: TextureAtlasSources,
257 atlas_handle: Handle<TextureAtlasLayout>,
258 vendor_handle: &Handle<Image>,
259) {
260 commands.spawn((
261 Transform {
262 translation: Vec3::new(translation.0, translation.1, translation.2),
263 scale: Vec3::splat(3.0),
264 ..default()
265 },
266 Sprite::from_atlas_image(
267 atlas_texture,
268 atlas_sources.handle(atlas_handle, vendor_handle).unwrap(),
269 ),
270 ));
271}
272
273fn create_label(
275 commands: &mut Commands,
276 translation: (f32, f32, f32),
277 text: &str,
278 text_style: TextFont,
279) {
280 commands.spawn((
281 Text2d::new(text),
282 text_style,
283 TextLayout::new_with_justify(JustifyText::Center),
284 Transform {
285 translation: Vec3::new(translation.0, translation.1, translation.2),
286 ..default()
287 },
288 ));
289}