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