1use bevy::{ecs::system::SystemId, prelude::*};
3
4use pipelines_ready::*;
5
6fn main() {
19 App::new()
20 .add_plugins(DefaultPlugins)
21 .add_plugins(PipelinesReadyPlugin)
23 .insert_resource(LoadingState::default())
24 .insert_resource(LoadingData::new(5))
25 .add_systems(Startup, (setup, load_loading_screen))
26 .add_systems(
27 Update,
28 (update_loading_data, level_selection, display_loading_screen),
29 )
30 .run();
31}
32
33#[derive(Resource, Default)]
35enum LoadingState {
36 #[default]
37 LevelReady,
38 LevelLoading,
39}
40
41#[derive(Resource, Debug, Default)]
43struct LoadingData {
44 loading_assets: Vec<UntypedHandle>,
46 confirmation_frames_target: usize,
50 confirmation_frames_count: usize,
52}
53
54impl LoadingData {
55 fn new(confirmation_frames_target: usize) -> Self {
56 Self {
57 loading_assets: Vec::new(),
58 confirmation_frames_target,
59 confirmation_frames_count: 0,
60 }
61 }
62}
63
64#[derive(Resource)]
66struct LevelData {
67 unload_level_id: SystemId,
68 level_1_id: SystemId,
69 level_2_id: SystemId,
70}
71
72fn setup(mut commands: Commands) {
73 let level_data = LevelData {
74 unload_level_id: commands.register_system(unload_current_level),
75 level_1_id: commands.register_system(load_level_1),
76 level_2_id: commands.register_system(load_level_2),
77 };
78 commands.insert_resource(level_data);
79
80 let text_style = TextFont {
82 font_size: FontSize::Px(42.0),
83 ..default()
84 };
85 commands
86 .spawn((
87 Node {
88 justify_self: JustifySelf::Center,
89 align_self: AlignSelf::FlexEnd,
90 ..default()
91 },
92 BackgroundColor(Color::NONE),
93 ))
94 .with_child((Text::new("Press 1 or 2 to load a new scene."), text_style));
95}
96
97fn level_selection(
99 mut commands: Commands,
100 keyboard: Res<ButtonInput<KeyCode>>,
101 level_data: Res<LevelData>,
102 loading_state: Res<LoadingState>,
103) {
104 if let LoadingState::LevelReady = loading_state.as_ref() {
106 if keyboard.just_pressed(KeyCode::Digit1) {
107 commands.run_system(level_data.unload_level_id);
108 commands.run_system(level_data.level_1_id);
109 } else if keyboard.just_pressed(KeyCode::Digit2) {
110 commands.run_system(level_data.unload_level_id);
111 commands.run_system(level_data.level_2_id);
112 }
113 }
114}
115
116#[derive(Component)]
118struct LevelComponents;
119
120fn unload_current_level(
122 mut commands: Commands,
123 mut loading_state: ResMut<LoadingState>,
124 entities: Query<Entity, With<LevelComponents>>,
125) {
126 *loading_state = LoadingState::LevelLoading;
127 for entity in entities.iter() {
128 commands.entity(entity).despawn();
129 }
130}
131
132fn load_level_1(
133 mut commands: Commands,
134 mut loading_data: ResMut<LoadingData>,
135 asset_server: Res<AssetServer>,
136) {
137 commands.spawn((
139 Camera3d::default(),
140 Transform::from_xyz(155.0, 155.0, 155.0).looking_at(Vec3::new(0.0, 40.0, 0.0), Vec3::Y),
141 LevelComponents,
142 ));
143
144 let fox = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb"));
146 loading_data.loading_assets.push(fox.clone().into());
147 commands.spawn((
149 WorldAssetRoot(fox.clone()),
150 Transform::from_xyz(0.0, 0.0, 0.0),
151 LevelComponents,
152 ));
153
154 commands.spawn((
156 DirectionalLight {
157 shadow_maps_enabled: true,
158 ..default()
159 },
160 Transform::from_xyz(3.0, 3.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
161 LevelComponents,
162 ));
163}
164
165fn load_level_2(
166 mut commands: Commands,
167 mut loading_data: ResMut<LoadingData>,
168 asset_server: Res<AssetServer>,
169) {
170 commands.spawn((
172 Camera3d::default(),
173 Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::new(0.0, 0.2, 0.0), Vec3::Y),
174 LevelComponents,
175 ));
176
177 let helmet_scene = asset_server
179 .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
180 loading_data
181 .loading_assets
182 .push(helmet_scene.clone().into());
183 commands.spawn((WorldAssetRoot(helmet_scene.clone()), LevelComponents));
184
185 commands.spawn((
187 DirectionalLight {
188 shadow_maps_enabled: true,
189 ..default()
190 },
191 Transform::from_xyz(3.0, 3.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
192 LevelComponents,
193 ));
194}
195
196fn update_loading_data(
198 mut loading_data: ResMut<LoadingData>,
199 mut loading_state: ResMut<LoadingState>,
200 asset_server: Res<AssetServer>,
201 pipelines_ready: Res<PipelinesReady>,
202) {
203 if !loading_data.loading_assets.is_empty() || !pipelines_ready.0 {
204 loading_data.confirmation_frames_count = 0;
207
208 loading_data.loading_assets.retain(|asset| {
209 asset_server
210 .get_recursive_dependency_load_state(asset)
211 .is_none_or(|state| !state.is_loaded())
212 });
213
214 } else {
219 loading_data.confirmation_frames_count += 1;
220 if loading_data.confirmation_frames_count == loading_data.confirmation_frames_target {
221 *loading_state = LoadingState::LevelReady;
222 }
223 }
224}
225
226#[derive(Component)]
228struct LoadingScreen;
229
230fn load_loading_screen(mut commands: Commands) {
232 let text_style = TextFont {
233 font_size: FontSize::Px(67.0),
234 ..default()
235 };
236
237 commands.spawn((
239 Camera2d,
240 Camera {
241 order: 1,
242 ..default()
243 },
244 LoadingScreen,
245 ));
246
247 commands
249 .spawn((
250 Node {
251 height: percent(100),
252 width: percent(100),
253 justify_content: JustifyContent::Center,
254 align_items: AlignItems::Center,
255 ..default()
256 },
257 BackgroundColor(Color::BLACK),
258 LoadingScreen,
259 ))
260 .with_child((Text::new("Loading..."), text_style.clone()));
261}
262
263fn display_loading_screen(
265 mut loading_screen: Single<&mut Visibility, (With<LoadingScreen>, With<Node>)>,
266 loading_state: Res<LoadingState>,
267) {
268 let visibility = match loading_state.as_ref() {
269 LoadingState::LevelLoading => Visibility::Visible,
270 LoadingState::LevelReady => Visibility::Hidden,
271 };
272
273 **loading_screen = visibility;
274}
275
276mod pipelines_ready {
277 use bevy::{
278 prelude::*,
279 render::{render_resource::*, *},
280 };
281
282 pub struct PipelinesReadyPlugin;
283 impl Plugin for PipelinesReadyPlugin {
284 fn build(&self, app: &mut App) {
285 app.insert_resource(PipelinesReady::default());
286
287 app.sub_app_mut(RenderApp)
293 .add_systems(ExtractSchedule, update_pipelines_ready);
294 }
295 }
296
297 #[derive(Resource, Debug, Default)]
298 pub struct PipelinesReady(pub bool);
299
300 fn update_pipelines_ready(mut main_world: ResMut<MainWorld>, pipelines: Res<PipelineCache>) {
301 if let Some(mut pipelines_ready) = main_world.get_resource_mut::<PipelinesReady>() {
302 pipelines_ready.0 = pipelines.waiting_pipelines().count() == 0;
303 }
304 }
305}