diff --git a/Cargo.toml b/Cargo.toml index 54887eb..a248392 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ iyes_perf_ui = { version = "0.3.0" } ron = { version = "0.8.1", optional = true } bevy_common_assets = { version = "0.11.0", features = ["ron"], optional = true } bevy-inspector-egui = { version = "0.27.0", features = ["highlight_changes"], optional = true } +bevy-trait-query = "0.6.0" [[bin]] name = "yahs" diff --git a/docs/devlog.md b/docs/devlog.md index 35a8410..e533332 100644 --- a/docs/devlog.md +++ b/docs/devlog.md @@ -1,5 +1,15 @@ # development log +## 2024-11-17 + +Time to see what's going on with the forces through some new UIs instead of +relying on the inspector plugin. It would be nice to have some gizmos that show +the forces as vectors in the scene. + +I added a new plugin that lets me query components by a trait. This greatly +simplifies the logic for collecting forces, and makes the coupling between the +force components and the equations that define them more direct. + ## 2024-11-16 I figured out how to get the forces to update correctly setting `WeightForce`, diff --git a/src/simulator/forces.rs b/src/simulator/forces.rs index 0ec1892..0d572df 100644 --- a/src/simulator/forces.rs +++ b/src/simulator/forces.rs @@ -4,6 +4,7 @@ use avian3d::prelude::*; use bevy::prelude::*; +use bevy_trait_query::{self, RegisterExt}; use super::{Atmosphere, Density, Mass, Volume}; @@ -18,6 +19,10 @@ impl Plugin for ForcesPlugin { app.register_type::(); app.register_type::(); + app.register_component_as::(); + app.register_component_as::(); + app.register_component_as::(); + // Disable the default forces since we apply our own. app.insert_resource(Gravity(Vec3::ZERO)); @@ -38,12 +43,16 @@ impl Plugin for ForcesPlugin { app.add_systems( Update, (update_weight_force, update_buoyant_force, update_drag_force) + .before(collect_forces) .in_set(ForceUpdateOrder::Prepare), ); app.add_systems( Update, update_total_external_force.in_set(ForceUpdateOrder::Apply), ); + + // for debugging, let's assume there will always be just one balloon. + app.init_resource::(); } } @@ -54,17 +63,44 @@ enum ForceUpdateOrder { Apply, } +#[derive(Bundle)] +pub struct ForceBundle { + collection: ForceCollection, + weight: WeightForce, + buoyancy: BuoyantForce, + drag: DragForce, +} + /// Add a `ForceCollection` to entities with a `RigidBody` when they are added. fn on_rigid_body_added(mut commands: Commands, query: Query>) { for entity in &query { - commands.entity(entity).insert((WeightForce::default(), BuoyantForce::default(), DragForce::default(), ForceCollection::default())); + commands.entity(entity).insert((ForceBundle { + collection: ForceCollection::default(), + weight: WeightForce::default(), + buoyancy: BuoyantForce::default(), + drag: DragForce::default(), + })); } } +/// This trait is used to identify a force component. +#[bevy_trait_query::queryable] +pub trait Force { + fn force(&self) -> Vec3 { self.0 } + fn update(&mut self); + fn direction(&self) -> Vec3 { self.force().normalize() } + fn magnitude(&self) -> f32 { self.force().length() } +} + /// Downward force (N) vector due to gravity as a function of altitude (m) and /// mass (kg). The direction of this force is always world-space down. #[derive(Component, Default, Reflect)] pub struct WeightForce(Vec3); +impl Force for WeightForce { + fn update(&mut self, position: Vec3, mass: f32) { + self.0 = weight(position, mass); + } +} /// Force (N) from gravity at an altitude (m) above mean sea level. fn g(position: Vec3) -> f32 { @@ -130,15 +166,30 @@ fn update_drag_force( /// populate it by emitting an event from each force system to populate the /// array. #[derive(Component, Default, Reflect)] -struct ForceCollection(Vec); +struct ForceCollection { + forces: Vec>, +} + +#[derive(Resource, Default, Reflect)] +struct NetForce(Vec3); + +/// Collect all the forces into the force collection. +fn collect_forces(mut forces: Query<(&mut ForceCollection, With>) { + for mut collection in forces.iter_mut() { + collection.0.push(Vec3::ZERO); + } +} /// Set the `ExternalForce` to the sum of all forces in the `Forces` collection. /// This effectively applies all the calculated force vectors to the physics /// rigid body without regard to where the forces came from. fn update_total_external_force( mut external_forces: Query<(&mut ExternalForce, &ForceCollection), With>, + // TODO: this is a temporary resource that assumes there is only balloon. + mut net_force: ResMut, ) { for (mut physics_force, forces) in external_forces.iter_mut() { - physics_force.set_force(forces.0.iter().sum()); + net_force.0 = forces.0.iter().sum(); + physics_force.set_force(net_force.0); } } diff --git a/src/ui/monitors.rs b/src/ui/monitors.rs new file mode 100644 index 0000000..70eedb2 --- /dev/null +++ b/src/ui/monitors.rs @@ -0,0 +1,16 @@ +//! UI for monitoring the simulation. + +use bevy::prelude::*; +use iyes_perf_ui::prelude::*; + +/// Plugin for the monitor UI. +pub struct MonitorPlugin; + +impl Plugin for MonitorPlugin { + fn build(&self, app: &mut App) { + app + // we must register our custom entry type + .add_perf_ui_simple_entry::() + .init_resource::(); + } +}