/
debug_plugin.rs
383 lines (352 loc) · 13.6 KB
/
debug_plugin.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
use std::{marker::PhantomData, time::Duration};
use bevy::{
app::{App, Plugin, PostStartup, PostUpdate, PreUpdate, Startup, Update},
ecs::{schedule::IntoSystemConfigs, system::Resource},
input::keyboard::KeyCode,
render::color::Color,
time::{Timer, TimerMode},
};
use bevy_ghx_grid::ghx_grid::coordinate_system::CoordinateSystem;
use self::{
cursor::{
deselect_from_keybinds, move_selection_from_keybinds, setup_cursor, setup_cursors_overlays,
setup_cursors_panel, switch_generation_selection_from_keybinds,
update_cursors_info_from_generation_events, update_cursors_info_on_cursors_changes,
update_cursors_overlays, update_selection_cursor_panel_text, CursorKeyboardMovement,
CursorKeyboardMovementSettings, SelectCursor, SelectionCursorMarkerSettings,
},
egui_editor::{editor_enabled, update_brush, BrushEvent, EditorConfig},
generation::{
generate_all, insert_error_markers_to_new_generations,
insert_void_nodes_to_new_generations, step_by_step_input_update, step_by_step_timed_update,
update_active_generation, update_generation_control, update_generation_view,
ActiveGeneration, GenerationEvent,
},
picking::{
picking_remove_previous_over_cursor, setup_picking_assets, update_cursor_targets_nodes,
update_over_cursor_from_generation_events, update_over_cursor_panel_text,
CursorTargetAssets, NodeOutEvent, OverCursor, OverCursorMarkerSettings,
},
};
use super::{
assets::NoComponents, insert_default_bundle_to_spawned_nodes, spawn_node, AssetSpawner,
AssetsBundleSpawner, ComponentSpawner,
};
#[cfg(feature = "picking")]
use bevy_mod_picking::PickableBundle;
#[cfg(feature = "picking")]
use self::picking::{
insert_cursor_picking_handlers_to_grid_nodes, picking_update_cursors_position, NodeOverEvent,
NodeSelectedEvent,
};
#[cfg(feature = "picking")]
pub mod picking;
#[cfg(feature = "egui-edit")]
use self::egui_editor::{draw_edition_panel, paint, update_painting_state, EditorContext};
#[cfg(feature = "egui-edit")]
pub mod egui_editor;
pub mod cursor;
pub mod generation;
#[derive(Default, Debug, PartialEq, Eq)]
pub enum CursorUiMode {
None,
Panel,
#[default]
Overlay,
}
#[derive(Resource, Debug)]
pub struct GridCursorsUiSettings {
pub font_size: f32,
pub background_color: Color,
pub text_color: Color,
}
impl Default for GridCursorsUiSettings {
fn default() -> Self {
Self {
font_size: 16.0,
background_color: Color::BLACK.with_a(0.45),
text_color: Color::WHITE,
}
}
}
/// A [`Plugin`] useful for debug/analysis/demo. It mainly run [`Generator`] components and spawn the generated model's [`crate::gen::assets::ModelAsset`]
///
/// It takes in a [`GenerationViewMode`] to control how the generators components will be run.
///
/// It also uses the following `Resources`: [`ProcGenKeyBindings`] and [`GenerationControl`] (and will init them to their defaults if not inserted by the user).
pub struct ProcGenDebugPlugin<
C: CoordinateSystem,
A: AssetsBundleSpawner,
T: ComponentSpawner = NoComponents,
> {
generation_view_mode: GenerationViewMode,
cursor_ui_mode: CursorUiMode,
typestate: PhantomData<(C, A, T)>,
}
impl<C: CoordinateSystem, A: AssetsBundleSpawner, T: ComponentSpawner> ProcGenDebugPlugin<C, A, T> {
/// Plugin constructor
pub fn new(generation_view_mode: GenerationViewMode, cursor_ui_mode: CursorUiMode) -> Self {
Self {
generation_view_mode,
cursor_ui_mode,
typestate: PhantomData,
}
}
}
impl<C: CoordinateSystem, A: AssetsBundleSpawner, T: ComponentSpawner> Plugin
for ProcGenDebugPlugin<C, A, T>
{
fn build(&self, app: &mut App) {
app.insert_resource(self.generation_view_mode);
app.insert_resource(ActiveGeneration::default());
// If the resources already exists, nothing happens, else, add them with default values.
app.init_resource::<ProcGenKeyBindings>()
.init_resource::<GenerationControl>()
.init_resource::<OverCursorMarkerSettings>()
.init_resource::<SelectionCursorMarkerSettings>()
.init_resource::<CursorKeyboardMovement>()
.init_resource::<CursorKeyboardMovementSettings>();
match self.cursor_ui_mode {
CursorUiMode::None => (),
_ => {
app.init_resource::<GridCursorsUiSettings>();
}
}
app.add_event::<GenerationEvent>();
#[cfg(feature = "egui-edit")]
app.init_resource::<EditorConfig>()
.init_resource::<EditorContext>()
.add_event::<BrushEvent>();
#[cfg(feature = "picking")]
app.init_resource::<CursorTargetAssets>()
.add_event::<NodeOverEvent>()
.add_event::<NodeOutEvent>()
.add_event::<NodeSelectedEvent>();
app
// PostStartup to wait for setup_cursors_overlays to be applied.
.add_systems(PostStartup, setup_cursor::<C, SelectCursor>)
// Keybinds and picking events handlers run in PreUpdate
.add_systems(
PreUpdate,
(
deselect_from_keybinds,
switch_generation_selection_from_keybinds::<C>,
move_selection_from_keybinds::<C>,
),
)
.add_systems(
Update,
(
update_generation_control,
update_active_generation::<C>,
update_cursors_info_on_cursors_changes::<C>,
),
)
.add_systems(PostUpdate, update_cursors_info_from_generation_events::<C>);
#[cfg(feature = "picking")]
app.add_systems(Startup, setup_picking_assets)
// PostStartup to wait for setup_cursors_overlays to be applied.
.add_systems(PostStartup, setup_cursor::<C, OverCursor>)
.add_systems(
Update,
(
insert_default_bundle_to_spawned_nodes::<PickableBundle>,
(
update_cursor_targets_nodes::<C>,
insert_cursor_picking_handlers_to_grid_nodes::<C>,
)
.chain(),
(
picking_remove_previous_over_cursor::<C>,
picking_update_cursors_position::<
C,
OverCursorMarkerSettings,
OverCursor,
NodeOverEvent,
>,
picking_update_cursors_position::<
C,
SelectionCursorMarkerSettings,
SelectCursor,
NodeSelectedEvent,
>,
)
.chain(),
),
)
.add_systems(
PostUpdate,
update_over_cursor_from_generation_events::<C>
.before(update_cursors_info_from_generation_events::<C>),
);
#[cfg(feature = "egui-edit")]
app.add_systems(
Update,
(
draw_edition_panel::<C>,
update_brush,
update_painting_state,
paint::<C>,
)
.chain()
.run_if(editor_enabled),
);
match self.cursor_ui_mode {
CursorUiMode::None => (),
CursorUiMode::Panel => {
app.add_systems(Startup, setup_cursors_panel);
app.add_systems(PostUpdate, update_selection_cursor_panel_text);
#[cfg(feature = "picking")]
app.add_systems(PostUpdate, update_over_cursor_panel_text);
}
CursorUiMode::Overlay => {
app.add_systems(Startup, setup_cursors_overlays);
app.add_systems(Update, update_cursors_overlays);
}
}
match self.generation_view_mode {
GenerationViewMode::StepByStepTimed {
steps_count,
interval_ms,
} => {
app.add_systems(
Update,
(
(
insert_error_markers_to_new_generations::<C>,
insert_void_nodes_to_new_generations::<C, A, T>,
),
step_by_step_timed_update::<C>,
update_generation_view::<C, A, T>,
)
.chain(),
);
app.insert_resource(StepByStepTimed {
steps_count,
timer: Timer::new(Duration::from_millis(interval_ms), TimerMode::Repeating),
});
}
GenerationViewMode::StepByStepManual => {
app.add_systems(
Update,
(
(
insert_error_markers_to_new_generations::<C>,
insert_void_nodes_to_new_generations::<C, A, T>,
),
step_by_step_input_update::<C>,
update_generation_view::<C, A, T>,
)
.chain(),
);
}
GenerationViewMode::Final => {
app.add_systems(
Update,
(generate_all::<C>, update_generation_view::<C, A, T>).chain(),
);
}
}
}
}
/// Controls how the generation occurs.
#[derive(Resource, Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenerationViewMode {
/// Generates steps by steps and waits at least the specified amount (in milliseconds) between each step.
StepByStepTimed {
/// How many steps to run once the timer has finished a cycle
steps_count: u32,
/// Time to wait in ms before the next steps
interval_ms: u64,
},
/// Generates step by step and waits for a user input between each step.
StepByStepManual,
/// Generates it all at once at the start
#[default]
Final,
}
/// Used to track the status of the generation control
#[derive(Resource, Eq, PartialEq, Debug)]
pub enum GenerationControlStatus {
/// Generation control is paused, systems won't automatically step the generation
Paused,
/// Generation control is "ongoing", systems can currently step a generator
Ongoing,
}
/// Read by the systems while generating
#[derive(Resource)]
pub struct GenerationControl {
/// Current status of the generation
pub status: GenerationControlStatus,
/// Indicates whether or not the generator needs to be reinitialized before calling generation operations.
///
/// When using [`GenerationViewMode::Final`], this only controls the first reinitialization per try pool.
pub need_reinit: bool,
/// Whether or not the spawning systems do one more generation step when nodes without assets are generated.
///
/// Not used when using [`GenerationViewMode::Final`].
pub skip_void_nodes: bool,
/// Whether or not the generation should pause when successful
pub pause_when_done: bool,
/// Whether or not the generation should pause when it fails.
///
/// When using [`GenerationViewMode::Final`], this only pauses on the last error of a try pool.
pub pause_on_error: bool,
/// Whether or not the generation should pause when it reinitializes
///
/// When using [`GenerationViewMode::Final`], this only pauses on the first reinitialization of a try pool.
pub pause_on_reinitialize: bool,
}
impl Default for GenerationControl {
fn default() -> Self {
Self {
status: GenerationControlStatus::Paused,
need_reinit: false,
skip_void_nodes: true,
pause_when_done: true,
pause_on_error: true,
pause_on_reinitialize: true,
}
}
}
/// Resource to track the generation steps when using [`GenerationViewMode::StepByStepTimed`]
#[derive(Resource)]
pub struct StepByStepTimed {
/// How many steps should be done once the timer has expired
pub steps_count: u32,
/// Timer, tracking the time between the steps
pub timer: Timer,
}
/// Resource available to override the default keybindings used by the [`ProcGenDebugPlugin`]
#[derive(Resource)]
pub struct ProcGenKeyBindings {
pub prev_node: KeyCode,
pub next_node: KeyCode,
pub cursor_x_axis: KeyCode,
pub cursor_y_axis: KeyCode,
pub cursor_z_axis: KeyCode,
pub deselect: KeyCode,
pub switch_grid: KeyCode,
/// Key to pause/unpause the current [`GenerationControlStatus`]
pub pause_toggle: KeyCode,
/// Key used only with [`GenerationViewMode::StepByStepPaused`] to step once per press
pub step: KeyCode,
/// Key used only with [`GenerationViewMode::StepByStepPaused`] to step continuously as long as pressed
pub continuous_step: KeyCode,
}
impl Default for ProcGenKeyBindings {
fn default() -> Self {
Self {
prev_node: KeyCode::ArrowLeft,
next_node: KeyCode::ArrowRight,
cursor_x_axis: KeyCode::KeyX,
cursor_y_axis: KeyCode::KeyY,
cursor_z_axis: KeyCode::KeyZ,
deselect: KeyCode::Escape,
switch_grid: KeyCode::Tab,
pause_toggle: KeyCode::Space,
step: KeyCode::ArrowDown,
continuous_step: KeyCode::ArrowUp,
}
}
}