Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui: add gizmos, flatten app module #3

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/devlog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
# development log

## 2024-11-27

Some of my dependencies may now have Bevy 0.15 support.

- [x] avian3d: (Jondolf/avian:main)[https://github.com/Jondolf/avian/tree/main]
- [ ] bevy_heavy: not supported yet
- [x] iyes_perf_ui:
(JohnathanFL/iyes_perf_ui:main)[https://github.com/JohnathanFL/iyes_perf_ui/tree/main]
([this is open PR from a fork](https://github.com/IyesGames/iyes_perf_ui/pull/22))
- [x] iyes_progress: `0.13.0-rc.1`
- [x] bevy-trait-query:
[JoJoJet/bevy-trait-query:bevy-0.15-rc](https://github.com/JoJoJet/bevy-trait-query/tree/bevy-0.15-rc)
(sort of supported, last updated for `bevy-0.15.0-rc.2`)
- [ ] bevy-inspector-egui: not supported yet

Here are some recent projects that I might be able to steal from:

- [bevy-motion-matching](https://github.com/kahboon0425/bevy_motion_matching) -
angular velocity debug gizmos and egui UI examples for plotting
- [siege](https://github.com/xenon615/siege) - rope example in Avian3d
- [bevy-logging](https://bevy-logging.github.io/) - advanced debug logging &
tracing example/tutorial

Now that the forces are working and look reasonable, I can start working on the
flight dynamics. I need to start by adding a way to speed up and slow down the
simulation time, because a real flight takes hours. Next I need to add plots so
I can monitor and validate the simulation results for things like altitude,
temperature, and pressure.

- [ ] Physics time multiplier.
- [ ] Plotting gas properties.
- [ ] Plotting balloon kinematics.

Bonus:

- [ ] Add a payload hanging from a tether.
- [ ] Camera follow the balloon, and maybe a scale or some reference in the
background to illustrate the balloon's size and altitude.

Stretch:

- [ ] Calculate drag forces and moments on arbitrary shapes.
- [ ] Add a wind field.

I figured out how to toggle the physics gizmos at runtime and added force gizmos
that I made myself.

## 2024-11-26

I added a `GasMonitor` to the UI for displaying the gas properties in real time.
Expand Down
20 changes: 10 additions & 10 deletions src/app3d/controls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ pub struct CameraControls {

#[derive(Reflect)]
pub struct DebugControls {
pub toggle_inspector: KeyCode,
pub toggle_wireframe: KeyCode,
pub toggle_physics_debug: KeyCode,
pub toggle_perf_ui: KeyCode,
pub toggle_anything_else: KeyCode,
pub toggle_1: KeyCode,
pub toggle_2: KeyCode,
pub toggle_3: KeyCode,
pub toggle_4: KeyCode,
pub toggle_5: KeyCode,
}

#[derive(Reflect)]
Expand All @@ -55,11 +55,11 @@ impl Default for CameraControls {
impl Default for DebugControls {
fn default() -> Self {
Self {
toggle_wireframe: KeyCode::F1,
toggle_inspector: KeyCode::F2,
toggle_physics_debug: KeyCode::F3,
toggle_perf_ui: KeyCode::F4,
toggle_anything_else: KeyCode::F5,
toggle_1: KeyCode::F1,
toggle_2: KeyCode::F2,
toggle_3: KeyCode::F3,
toggle_4: KeyCode::F4,
toggle_5: KeyCode::F5,
}
}
}
Expand Down
103 changes: 54 additions & 49 deletions src/app3d/ui/dev_tools.rs → src/app3d/dev_tools.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Development tools for the game. This plugin is only enabled in dev builds.
use avian3d::debug_render::PhysicsDebugPlugin;
use avian3d::debug_render::*;
#[cfg(not(target_arch = "wasm32"))]
use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin};
#[allow(unused_imports)]
Expand All @@ -13,13 +13,15 @@ use bevy::{
input::common_conditions::input_just_pressed,
prelude::*,
};
use iyes_perf_ui::prelude::*;

use crate::{app3d::controls::KeyBindingsConfig, simulator::SimState};

pub(super) fn plugin(app: &mut App) {
// Toggle the debug overlay for UI.
app.add_plugins((
pub struct DevToolsPlugin;

impl Plugin for DevToolsPlugin {
fn build(&self, app: &mut App) {
// Toggle the debug overlay for UI.
app.add_plugins((
// physics
PhysicsDebugPlugin::default(),
// performance
Expand All @@ -31,12 +33,9 @@ pub(super) fn plugin(app: &mut App) {
));

app.init_resource::<DebugState>();
app.add_event::<SpawnPerfUi>();
app.add_event::<DespawnPerfUi>();
app.add_observer(spawn_perf_ui);
app.add_observer(despawn_perf_ui);

app.add_systems(Update, log_transitions::<SimState>);
app.add_systems(Update, show_physics_gizmos);

// Wireframe doesn't work on WASM
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -46,13 +45,39 @@ pub(super) fn plugin(app: &mut App) {
// use bevy_inspector_egui::quick::WorldInspectorPlugin;
// app.add_plugins(WorldInspectorPlugin::new());
// }
}
}

#[derive(Debug, Default, Resource)]
#[derive(Debug, Resource)]
struct DebugState {
wireframe: bool,
physics_debug: bool,
perf_ui: bool,
forces: bool,
physics: bool,
}

impl Default for DebugState {
fn default() -> Self {
Self {
wireframe: false,
forces: true,
physics: false,
}
}
}

impl DebugState {
fn toggle_wireframe(&mut self) {
self.wireframe = !self.wireframe;
warn!("wireframe debug: {}", self.wireframe);
}
fn toggle_forces(&mut self) {
self.forces = !self.forces;
warn!("forces debug: {}", self.forces);
}
fn toggle_physics(&mut self) {
self.physics = !self.physics;
warn!("physics debug: {}", self.physics);
}
}

#[allow(dead_code)]
Expand All @@ -61,53 +86,33 @@ struct DebugUi;

#[cfg(not(target_arch = "wasm32"))]
fn toggle_debug_ui(
mut commands: Commands,
mut wireframe_config: ResMut<WireframeConfig>,
mut debug_state: ResMut<DebugState>,
key_input: Res<ButtonInput<KeyCode>>,
key_bindings: Res<KeyBindingsConfig>,
) {
if key_input.just_pressed(key_bindings.debug_controls.toggle_wireframe) {
debug_state.wireframe = !debug_state.wireframe;
if key_input.just_pressed(key_bindings.debug_controls.toggle_1) {
debug_state.toggle_wireframe();
wireframe_config.global = !wireframe_config.global;
warn!("wireframe: {}", debug_state.wireframe);
}

if key_input.just_pressed(key_bindings.debug_controls.toggle_physics_debug) {
debug_state.physics_debug = !debug_state.physics_debug;
warn!("physics debug: {} - not implemented", debug_state.physics_debug);
if key_input.just_pressed(key_bindings.debug_controls.toggle_2) {
debug_state.toggle_forces();
}

if key_input.just_pressed(key_bindings.debug_controls.toggle_perf_ui) {
debug_state.perf_ui = !debug_state.perf_ui;
warn!("perf ui: {}", debug_state.perf_ui);
if debug_state.perf_ui {
commands.trigger(SpawnPerfUi);
} else {
commands.trigger(DespawnPerfUi);
}
if key_input.just_pressed(key_bindings.debug_controls.toggle_3) {
debug_state.toggle_physics();
}
}

#[derive(Event, Default)]
struct SpawnPerfUi;

fn spawn_perf_ui(_trigger: Trigger<SpawnPerfUi>, mut commands: Commands) {
info!("spawn_perf_ui");
warn!("spawning perf ui DOES NOT WORK");
commands.spawn((DebugUi,
PerfUiRoot::default(),
PerfUiEntryFPS::default(),
PerfUiEntryClock::default(),
));
}

#[derive(Event, Default)]
struct DespawnPerfUi;

fn despawn_perf_ui(_trigger: Trigger<DespawnPerfUi>, mut commands: Commands, ui: Query<Entity, (With<DebugUi>, With<PerfUiRoot>)>) {
info!("despawn_perf_ui");
for ui in ui.iter() {
commands.entity(ui).despawn_recursive();
fn show_physics_gizmos(
debug_state: Res<DebugState>,
mut gizmo_store: ResMut<GizmoConfigStore>
) {
if gizmo_store.is_changed() {
let (_, physics_config) = gizmo_store.config_mut::<PhysicsGizmos>();
if debug_state.physics {
*physics_config = PhysicsGizmos::all();
} else {
*physics_config = PhysicsGizmos::none();
}
}
}
24 changes: 24 additions & 0 deletions src/app3d/gizmos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use bevy::{color::palettes::basic::*, prelude::*};

use crate::simulator::{forces::Force, SimState};

Check failure on line 3 in src/app3d/gizmos.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `SimState`

const ARROW_SCALE: f32 = 0.1;

pub struct ForceArrowsPlugin;

impl Plugin for ForceArrowsPlugin {
fn build(&self, app: &mut App) {
app.add_systems(PostUpdate, force_arrows);
}
}

fn force_arrows(query: Query<&dyn Force>, mut gizmos: Gizmos) {
for forces in query.iter() {
for force in forces.iter() {
let start = force.point_of_application();
let end = start + force.force() * ARROW_SCALE;
let color = force.color().unwrap_or(RED.into());
gizmos.arrow(start, end, color).with_tip_length(0.1);
}
}
}
56 changes: 50 additions & 6 deletions src/app3d/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
mod scene;
mod camera;
mod ui;
pub mod controls;
mod gizmos;
mod scene;
mod dev_tools;
mod monitors;

use scene::ScenePlugin;
use ui::InterfacePlugin;
use controls::ControlsPlugin;
use camera::CameraPlugin;
use controls::{ControlsPlugin, KeyBindingsConfig};
use gizmos::ForceArrowsPlugin;
use scene::ScenePlugin;
use dev_tools::DevToolsPlugin;
use monitors::MonitorsPlugin;

use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::{app::{PluginGroup, PluginGroupBuilder}, prelude::*};

use crate::simulator::SimState;

pub struct App3dPlugins;

Expand All @@ -21,3 +27,41 @@ impl PluginGroup for App3dPlugins {
.add(CameraPlugin)
}
}

/// A plugin group that includes all interface-related plugins
pub struct InterfacePlugin;

impl Plugin for InterfacePlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
PausePlayPlugin,
ForceArrowsPlugin,
MonitorsPlugin,
#[cfg(feature = "dev")]
DevToolsPlugin,
));
}
}

pub struct PausePlayPlugin;

impl Plugin for PausePlayPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, toggle_pause);
}
}

fn toggle_pause(
sim_state: Res<State<SimState>>,
mut next_state: ResMut<NextState<SimState>>,
key_input: Res<ButtonInput<KeyCode>>,
key_bindings: Res<KeyBindingsConfig>,
) {
if key_input.just_pressed(key_bindings.time_controls.toggle_pause) {
match sim_state.as_ref().get() {
SimState::Stopped => next_state.set(SimState::Running),
SimState::Running => next_state.set(SimState::Stopped),
_ => next_state.set(SimState::Running)
}
}
}
6 changes: 3 additions & 3 deletions src/app3d/ui/monitors.rs → src/app3d/monitors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ impl PerfUiEntry for SimStateMonitor {

fn format_value(&self, value: &Self::Value) -> String {
match value {
SimState::Loading => String::from("Loading"),
SimState::Running => String::from("Running"),
SimState::Stopped => String::from("Stopped"),
_ => String::from("Unknown"),
}
}

Expand Down Expand Up @@ -118,8 +118,8 @@ impl PerfUiEntry for SimStateMonitor {
fn value_color(&self, value: &Self::Value) -> Option<Color> {
match *value {
SimState::Running => self.color_gradient.get_color_for_value(0.0),
SimState::Stopped => self.color_gradient.get_color_for_value(10.0),
_ => self.color_gradient.get_color_for_value(5.0),
SimState::Stopped => self.color_gradient.get_color_for_value(5.0),
_ => self.color_gradient.get_color_for_value(10.0),
}
}

Expand Down
Loading
Loading