Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
philiplinden committed Nov 16, 2024
1 parent f305d5b commit e4d7121
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 10 additions & 0 deletions docs/devlog.md
Original file line number Diff line number Diff line change
@@ -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`,
Expand Down
57 changes: 54 additions & 3 deletions src/simulator/forces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use avian3d::prelude::*;
use bevy::prelude::*;
use bevy_trait_query::{self, RegisterExt};

use super::{Atmosphere, Density, Mass, Volume};

Expand All @@ -18,6 +19,10 @@ impl Plugin for ForcesPlugin {
app.register_type::<BuoyantForce>();
app.register_type::<DragForce>();

app.register_component_as::<dyn Force, WeightForce>();
app.register_component_as::<dyn Force, BuoyantForce>();
app.register_component_as::<dyn Force, DragForce>();

// Disable the default forces since we apply our own.
app.insert_resource(Gravity(Vec3::ZERO));

Expand All @@ -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::<NetForce>();
}
}

Expand All @@ -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<Entity, Added<RigidBody>>) {
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 {
Expand Down Expand Up @@ -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<Vec3>);
struct ForceCollection {
forces: Vec<Box<dyn Force>>,
}

#[derive(Resource, Default, Reflect)]
struct NetForce(Vec3);

/// Collect all the forces into the force collection.
fn collect_forces(mut forces: Query<(&mut ForceCollection, With<RigidBody>>) {
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<RigidBody>>,
// TODO: this is a temporary resource that assumes there is only balloon.
mut net_force: ResMut<NetForce>,
) {
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);
}
}
16 changes: 16 additions & 0 deletions src/ui/monitors.rs
Original file line number Diff line number Diff line change
@@ -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::<PerfUiTimeSinceLastClick>()
.init_resource::<TimeSinceLastClick>();
}
}

0 comments on commit e4d7121

Please sign in to comment.