forked from Brickworks/yahs
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ui: add gizmos, flatten app module * debug: cleanup and gizmos * physics: time control * ci: skip deploys
- Loading branch information
1 parent
119dade
commit ae590a3
Showing
17 changed files
with
438 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,114 @@ | ||
use bevy::prelude::*; | ||
use bevy::{ | ||
input::mouse::{MouseScrollUnit, MouseWheel}, | ||
prelude::*, | ||
}; | ||
|
||
// use crate::controls::CameraControls; | ||
use super::controls::KeyBindingsConfig; | ||
use crate::simulator::Balloon; | ||
|
||
const INVERT_ZOOM: bool = true; | ||
|
||
pub struct CameraPlugin; | ||
|
||
impl Plugin for CameraPlugin { | ||
fn build(&self, app: &mut App) { | ||
app.init_resource::<CameraSelection>(); | ||
app.add_systems(Startup, setup); | ||
app.add_systems(Update, zoom_camera); | ||
app.add_plugins(CameraFollowPlugin); | ||
} | ||
} | ||
|
||
#[derive(Component, Default)] | ||
#[require(Camera3d, PerspectiveProjection)] | ||
struct MainCamera; | ||
|
||
/// A resource that stores the currently selected camera target. | ||
#[derive(Resource)] | ||
struct CameraSelection { | ||
entity: Entity, | ||
offset: Vec3, | ||
} | ||
|
||
impl Default for CameraSelection { | ||
fn default() -> Self { | ||
Self { | ||
entity: Entity::PLACEHOLDER, | ||
offset: Vec3::new(0., 0., 10.), | ||
} | ||
} | ||
} | ||
|
||
/// A marker component for entities that can be selected as a camera target. | ||
#[derive(Component, Default, Reflect)] | ||
pub struct CameraTarget; | ||
|
||
fn setup(mut commands: Commands) { | ||
commands.spawn(( | ||
// Note we're setting the initial position below with yaw, pitch, and radius, hence | ||
// we don't set transform on the camera. | ||
Name::new("Main Camera"), | ||
MainCamera, | ||
Camera3d::default(), | ||
Transform::from_xyz(0.0, 20., 50.0).looking_at(Vec3::new(0., 20., 0.), Vec3::Y), | ||
)); | ||
} | ||
|
||
fn zoom_camera( | ||
mut camera: Query<&mut PerspectiveProjection, (With<Camera3d>, With<MainCamera>)>, | ||
mut evr_scroll: EventReader<MouseWheel>, | ||
key_bindings: Res<KeyBindingsConfig>, | ||
) { | ||
let mut projection = camera.single_mut(); | ||
let ctrl = &key_bindings.camera_controls; | ||
let direction = if INVERT_ZOOM { -1.0 } else { 1.0 }; | ||
for ev in evr_scroll.read() { | ||
match ev.unit { | ||
MouseScrollUnit::Line => { | ||
projection.fov = projection.fov.clamp(ctrl.min_fov, ctrl.max_fov) | ||
+ ev.y * ctrl.zoom_step * direction; | ||
} | ||
MouseScrollUnit::Pixel => { | ||
projection.fov = projection.fov.clamp(ctrl.min_fov, ctrl.max_fov) | ||
+ ev.y * ctrl.zoom_step * direction; | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct CameraFollowPlugin; | ||
|
||
impl Plugin for CameraFollowPlugin { | ||
fn build(&self, app: &mut App) { | ||
app.add_systems(Update, (mark_new_targets, follow_selected_target)); | ||
} | ||
} | ||
|
||
fn mark_new_targets( | ||
mut commands: Commands, | ||
balloons: Query<Entity, Added<Balloon>>, | ||
mut selection: ResMut<CameraSelection>, | ||
) { | ||
for entity in &balloons { | ||
commands.entity(entity).insert(CameraTarget); | ||
// Focus on the newest balloon | ||
selection.entity = entity; | ||
} | ||
} | ||
|
||
fn follow_selected_target( | ||
selection: Res<CameraSelection>, | ||
targets: Query<&Transform, (With<CameraTarget>, Without<MainCamera>)>, | ||
mut camera: Query<&mut Transform, With<MainCamera>>, | ||
) { | ||
let mut cam = camera.single_mut(); | ||
match targets.get(selection.entity) { | ||
Ok(t) => { | ||
// If the target exists, move the camera next to it | ||
cam.translation = t.translation + selection.offset; | ||
// Look at the target position | ||
cam.look_at(t.translation, Vec3::Y); | ||
} | ||
Err(_) => { | ||
// If there is no selected entity, stay where you are | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,83 @@ | ||
use bevy::{color::palettes::basic::*, prelude::*}; | ||
|
||
use crate::simulator::{forces::Force, SimState}; | ||
use crate::simulator::forces::Force; | ||
|
||
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); | ||
app.init_gizmo_group::<ForceGizmos>(); | ||
app.register_type::<ForceGizmos>(); | ||
app.add_systems( | ||
PostUpdate, | ||
force_arrows.run_if( | ||
|store: Res<GizmoConfigStore>| { | ||
store.config::<ForceGizmos>().0.enabled | ||
}), | ||
); | ||
} | ||
} | ||
|
||
fn force_arrows(query: Query<&dyn Force>, mut gizmos: Gizmos) { | ||
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); | ||
let color = match force.color() { | ||
Some(c) => c, | ||
None => RED.into(), | ||
}; | ||
gizmos.arrow(start, end, color).with_tip_length(0.3); | ||
} | ||
} | ||
} | ||
|
||
#[derive(Reflect, GizmoConfigGroup)] | ||
pub struct ForceGizmos { | ||
/// The scale of the force arrows. | ||
pub arrow_scale: Option<f32>, | ||
/// The color of the force arrows. If `None`, the arrows will not be rendered. | ||
pub arrow_color: Option<Color>, | ||
/// The length of the arrow tips. | ||
pub tip_length: Option<f32>, | ||
/// Determines if the forces should be hidden when not active. | ||
pub enabled: bool, | ||
} | ||
|
||
impl Default for ForceGizmos { | ||
fn default() -> Self { | ||
Self { | ||
arrow_scale: Some(0.1), | ||
arrow_color: Some(RED.into()), | ||
tip_length: Some(0.3), | ||
enabled: false, | ||
} | ||
} | ||
} | ||
|
||
impl ForceGizmos { | ||
/// Creates a [`ForceGizmos`] configuration with all rendering options enabled. | ||
pub fn all() -> Self { | ||
Self { | ||
arrow_scale: Some(0.1), | ||
arrow_color: Some(RED.into()), | ||
tip_length: Some(0.3), | ||
enabled: true, | ||
} | ||
} | ||
|
||
/// Creates a [`ForceGizmos`] configuration with debug rendering enabled but all options turned off. | ||
pub fn none() -> Self { | ||
Self { | ||
arrow_scale: None, | ||
arrow_color: None, | ||
tip_length: None, | ||
enabled: false, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.