From e697b7705c5c98296936bd6f94c5b7e3f8338605 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Fri, 9 Jun 2023 22:20:33 +0100 Subject: [PATCH 1/7] Display arrows from factory spawn to target (closes #434) --- assets/shaders/rally_point.wgsl | 36 +++ crates/construction/src/manufacturing.rs | 17 +- .../controller/src/selection/bookkeeping.rs | 6 +- crates/signs/src/lib.rs | 4 + crates/signs/src/line.rs | 268 ++++++++++++++++++ 5 files changed, 324 insertions(+), 7 deletions(-) create mode 100644 assets/shaders/rally_point.wgsl create mode 100644 crates/signs/src/line.rs diff --git a/assets/shaders/rally_point.wgsl b/assets/shaders/rally_point.wgsl new file mode 100644 index 00000000..2698f1c5 --- /dev/null +++ b/assets/shaders/rally_point.wgsl @@ -0,0 +1,36 @@ +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_bindings + +struct CustomMaterial { + color: vec4, + pointiness: f32, + speed: f32, + length: f32, + spacing: f32, + fade: f32, +}; + +@group(1) @binding(0) +var material: CustomMaterial; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { + let world_space_length: f32 = length(mesh.model[0].xyz); + let scaled_x: f32 = uv.x * world_space_length; + let offset_y: f32 = abs(uv.y - 0.5) * material.pointiness; + let scaled_time: f32 = globals.time * material.speed; + let total_length = material.length + material.spacing; + + let value = scaled_x + offset_y - scaled_time; + // Ensure that the result of the modulo operation is always positive + let positive_modulo = (value % total_length + total_length) % total_length; + let alpha = step(material.spacing, positive_modulo); + + let start_fade: f32 = (floor(value / total_length) * total_length + scaled_time) / material.fade; + let end_fade: f32 = (world_space_length - ((ceil(value / total_length) * total_length + scaled_time))) / material.fade; + let fade = min(1., min(start_fade, end_fade)); + + return material.color * vec4(1., 1., 1., alpha * fade); +} diff --git a/crates/construction/src/manufacturing.rs b/crates/construction/src/manufacturing.rs index c560b342..a7f12c86 100644 --- a/crates/construction/src/manufacturing.rs +++ b/crates/construction/src/manufacturing.rs @@ -15,7 +15,7 @@ use de_core::{ use de_index::SpatialQuery; use de_objects::SolidObjects; use de_pathing::{PathQueryProps, PathTarget, UpdateEntityPath}; -use de_signs::UpdatePoleLocationEvent; +use de_signs::{UpdateLineEndEvent, UpdateLineLocationEvent, UpdatePoleLocationEvent}; use de_spawner::{ObjectCounter, SpawnBundle}; use parry2d::bounding_volume::Aabb; use parry3d::math::Isometry; @@ -309,13 +309,17 @@ fn configure( solids: SolidObjects, new: Query<(Entity, &Transform, &ObjectType), Added>, mut pole_events: EventWriter, + mut line_events: EventWriter, ) { for (entity, transform, &object_type) in new.iter() { let solid = solids.get(object_type); - if solid.factory().is_some() { + if let Some(factory) = solid.factory() { + let start = transform.transform_point(factory.position().to_msl()); let local_aabb = solid.ichnography().local_aabb(); let delivery_location = DeliveryLocation::initial(local_aabb, transform); pole_events.send(UpdatePoleLocationEvent::new(entity, delivery_location.0)); + let end = delivery_location.0.to_msl(); + line_events.send(UpdateLineLocationEvent::new(entity, start, end)); commands .entity(entity) .insert((AssemblyLine::default(), delivery_location)); @@ -327,14 +331,15 @@ fn change_locations( mut events: EventReader, mut locations: Query<&mut DeliveryLocation>, mut pole_events: EventWriter, + mut line_events: EventWriter, ) { for event in events.iter() { if let Ok(mut location) = locations.get_mut(event.factory()) { + let owner = event.factory(); location.0 = event.position(); - pole_events.send(UpdatePoleLocationEvent::new( - event.factory(), - event.position(), - )); + pole_events.send(UpdatePoleLocationEvent::new(owner, event.position())); + let end = event.position().to_msl(); + line_events.send(UpdateLineEndEvent::new(owner, end)); } } } diff --git a/crates/controller/src/selection/bookkeeping.rs b/crates/controller/src/selection/bookkeeping.rs index 63249bb3..a535a880 100644 --- a/crates/controller/src/selection/bookkeeping.rs +++ b/crates/controller/src/selection/bookkeeping.rs @@ -1,7 +1,7 @@ use ahash::AHashSet; use bevy::{ecs::system::SystemParam, prelude::*}; use de_core::{baseset::GameSet, gamestate::GameState}; -use de_signs::{UpdateBarVisibilityEvent, UpdatePoleVisibilityEvent}; +use de_signs::{UpdateBarVisibilityEvent, UpdateLineVisibilityEvent, UpdatePoleVisibilityEvent}; use de_terrain::MarkerVisibility; use crate::SELECTION_BAR_ID; @@ -167,6 +167,7 @@ fn selected_system( mut markers: Query<&mut MarkerVisibility>, mut bars: EventWriter, mut poles: EventWriter, + mut lines: EventWriter, ) { for event in events.iter() { if let Ok(mut visibility) = markers.get_mut(event.0) { @@ -180,6 +181,7 @@ fn selected_system( )); poles.send(UpdatePoleVisibilityEvent::new(event.0, true)); + lines.send(UpdateLineVisibilityEvent::new(event.0, true)); } } @@ -188,6 +190,7 @@ fn deselected_system( mut markers: Query<&mut MarkerVisibility>, mut bars: EventWriter, mut poles: EventWriter, + mut lines: EventWriter, ) { for event in events.iter() { if let Ok(mut visibility) = markers.get_mut(event.0) { @@ -201,5 +204,6 @@ fn deselected_system( )); poles.send(UpdatePoleVisibilityEvent::new(event.0, false)); + lines.send(UpdateLineVisibilityEvent::new(event.0, false)); } } diff --git a/crates/signs/src/lib.rs b/crates/signs/src/lib.rs index 12234070..9c44a175 100644 --- a/crates/signs/src/lib.rs +++ b/crates/signs/src/lib.rs @@ -1,11 +1,14 @@ use bars::BarsPlugin; pub use bars::{UpdateBarValueEvent, UpdateBarVisibilityEvent}; use bevy::{app::PluginGroupBuilder, prelude::*}; +use line::LinePlugin; +pub use line::{UpdateLineEndEvent, UpdateLineLocationEvent, UpdateLineVisibilityEvent}; use markers::MarkersPlugin; use pole::PolePlugin; pub use pole::{UpdatePoleLocationEvent, UpdatePoleVisibilityEvent}; mod bars; +mod line; mod markers; mod pole; @@ -22,5 +25,6 @@ impl PluginGroup for SignsPluginGroup { .add(BarsPlugin) .add(MarkersPlugin) .add(PolePlugin) + .add(LinePlugin) } } diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs new file mode 100644 index 00000000..016f0f1e --- /dev/null +++ b/crates/signs/src/line.rs @@ -0,0 +1,268 @@ +use std::collections::hash_map::Entry; + +use ahash::AHashMap; +use bevy::prelude::*; +use bevy::reflect::TypeUuid; +use bevy::render::render_resource::{AsBindGroup, ShaderRef}; +use de_core::baseset::GameSet; +use de_core::state::AppState; + +/// Width of the line that goes to the pole. +const LINE_WIDTH: f32 = 1.; +/// Offset above mean sea level of the line, stopping z-fighting with the floor. +const LINE_OFFSET: Vec3 = Vec3::new(0., 1e-3, 0.); +/// Material configuration used for the lines to the factory spawn point +const LINE_MATERIAL: LineMaterial = LineMaterial { + color: Color::rgba(0.0, 0.5, 0.0, 0.8), + pointiness: 2., + speed: 3., + length: 1., + spacing: 0.5, + fade: 3., + alpha_mode: AlphaMode::Blend, +}; + +pub(crate) struct LinePlugin; + +impl Plugin for LinePlugin { + fn build(&self, app: &mut App) { + app.add_plugin(MaterialPlugin::::default()) + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_system(setup.in_schedule(OnEnter(AppState::InGame))) + .add_system(cleanup.in_schedule(OnExit(AppState::InGame))) + .add_system( + update_line_end + .in_base_set(GameSet::PostUpdate) + .run_if(in_state(AppState::InGame)) + .run_if(on_event::()) + .in_set(LinesSet::LineEnd), + ) + .add_system( + update_line_location + .in_base_set(GameSet::PostUpdate) + .run_if(in_state(AppState::InGame)) + .run_if(on_event::()) + .in_set(LinesSet::LocationEvents) + .after(LinesSet::LineEnd), + ) + .add_system( + update_line_visibility + .in_base_set(GameSet::PostUpdate) + .run_if(in_state(AppState::InGame)) + .run_if(on_event::()) + .in_set(LinesSet::VisibilityEvents), + ) + .add_system( + spawn_line + .in_base_set(GameSet::PostUpdate) + .run_if(in_state(AppState::InGame)) + .run_if(on_event::()) + .in_set(LinesSet::SpawnLines) + .after(LinesSet::VisibilityEvents), + ) + .add_system( + despawn_line + .in_base_set(GameSet::PostUpdate) + .run_if(in_state(AppState::InGame)) + .run_if(on_event::()) + .in_set(LinesSet::SpawnLines) + .after(LinesSet::VisibilityEvents), + ); + } +} + +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)] +enum LinesSet { + LineEnd, + LocationEvents, + VisibilityEvents, + SpawnLines, +} + +#[derive(Resource)] +struct LineMesh(Handle); + +fn setup(mut commands: Commands, mut meshes: ResMut>) { + commands.init_resource::(); + commands.init_resource::(); + let line_mesh = meshes.add(shape::Plane::from_size(1.0).into()); + commands.insert_resource(LineMesh(line_mesh)); +} + +fn cleanup(mut commands: Commands) { + commands.remove_resource::(); + commands.remove_resource::(); + commands.remove_resource::(); +} + +pub struct UpdateLineVisibilityEvent { + owner: Entity, + visible: bool, +} + +impl UpdateLineVisibilityEvent { + pub fn new(owner: Entity, visible: bool) -> Self { + Self { owner, visible } + } +} + +pub struct UpdateLineLocationEvent { + owner: Entity, + start: Vec3, + end: Vec3, +} + +impl UpdateLineLocationEvent { + pub fn new(owner: Entity, start: Vec3, end: Vec3) -> Self { + Self { owner, start, end } + } +} + +pub struct UpdateLineEndEvent { + owner: Entity, + end: Vec3, +} + +impl UpdateLineEndEvent { + pub fn new(owner: Entity, end: Vec3) -> Self { + Self { owner, end } + } +} + +struct DespawnLineEvent { + owner: Entity, +} + +struct SpawnLineEvent(Transform, Entity); + +#[derive(Resource, Default)] +struct LineEntities(AHashMap); + +#[derive(Resource, Default)] +struct LineTransforms(AHashMap); + +fn update_line_end( + mut events: EventReader, + lines: Res, + mut line_location: EventWriter, +) { + for event in &mut events { + if let Some([start, _end]) = lines.0.get(&event.owner) { + line_location.send(UpdateLineLocationEvent::new(event.owner, *start, event.end)); + } + } +} + +fn update_line_location( + lines: Res, + mut events: EventReader, + mut transforms: Query<&mut Transform>, + mut line_transforms: ResMut, +) { + for event in &mut events { + let transform = Transform::from_matrix(compute_line_transform(event.start, event.end)); + let positions = [event.start, event.end]; + line_transforms.0.insert(event.owner, positions); + if let Some(line_entity) = lines.0.get(&event.owner) { + let mut current_transform = transforms.get_mut(*line_entity).unwrap(); + *current_transform = transform + } + } +} + +fn update_line_visibility( + mut events: EventReader, + mut lines: ResMut, + line_transforms: Res, + mut spawn_line_events: EventWriter, + mut despawn_line_events: EventWriter, +) { + for event in &mut events { + let line_entity = lines.0.entry(event.owner); + if event.visible && matches!(line_entity, Entry::Vacant(_)) { + if let Some([start, end]) = line_transforms.0.get(&event.owner) { + let transform = Transform::from_matrix(compute_line_transform(*start, *end)); + spawn_line_events.send(SpawnLineEvent(transform, event.owner)); + } + } else if !event.visible { + let owner = event.owner; + despawn_line_events.send(DespawnLineEvent { owner }); + } + } +} + +fn spawn_line( + mut lines: ResMut, + mut events: EventReader, + mut commands: Commands, + line_mesh: Res, + mut materials: ResMut>, +) { + for SpawnLineEvent(transform, owner) in &mut events { + let line_id = commands + .spawn(MaterialMeshBundle { + mesh: line_mesh.0.clone(), + transform: *transform, + material: materials.add(LINE_MATERIAL), + ..default() + }) + .id(); + lines.0.insert(*owner, line_id); + } +} + +fn despawn_line( + mut commands: Commands, + mut events: EventReader, + mut lines: ResMut, +) { + for event in &mut events { + if let Some(line_entity) = lines.0.remove(&event.owner) { + commands.entity(line_entity).despawn_recursive(); + } + } +} + +/// A transform matrix from a plane with points at `(-0.5, 0. -0.5),(0.5, 0. -0.5),(0.5, 0. 0.5),(-0.5, 0.-0.5)` to the line start and end with the desired width +fn compute_line_transform(line_start: Vec3, end: Vec3) -> Mat4 { + let line_direction = end - line_start; + let perpendicular_direction = + Vec3::new(-line_direction.z, line_direction.y, line_direction.x).normalize() * LINE_WIDTH; + let x_axis = line_direction.extend(0.); + let z_axis = perpendicular_direction.extend(0.); + let w_axis = (line_start + line_direction / 2. + LINE_OFFSET).extend(1.); + Mat4::from_cols(x_axis, Vec4::Y, z_axis, w_axis) +} + +impl Material for LineMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/rally_point.wgsl".into() + } + + fn alpha_mode(&self) -> AlphaMode { + self.alpha_mode + } +} + +// Passed to the `rally_point.wgsl` shader +#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[uuid = "d0fae52d-f398-4416-9b72-9039093a6c34"] +pub struct LineMaterial { + #[uniform(0)] + color: Color, + #[uniform(0)] + pointiness: f32, + #[uniform(0)] + speed: f32, + #[uniform(0)] + length: f32, + #[uniform(0)] + spacing: f32, + #[uniform(0)] + fade: f32, + alpha_mode: AlphaMode, +} From 8b03b898b188a53b38fc4669867bf450f70ec114 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Tue, 20 Jun 2023 19:21:08 +0100 Subject: [PATCH 2/7] Another code review change --- assets/shaders/rally_point.wgsl | 21 ++- crates/construction/src/manufacturing.rs | 9 +- crates/signs/src/lib.rs | 4 +- crates/signs/src/line.rs | 229 ++++++++++------------- 4 files changed, 123 insertions(+), 140 deletions(-) diff --git a/assets/shaders/rally_point.wgsl b/assets/shaders/rally_point.wgsl index 2698f1c5..f52d0142 100644 --- a/assets/shaders/rally_point.wgsl +++ b/assets/shaders/rally_point.wgsl @@ -13,24 +13,31 @@ struct CustomMaterial { @group(1) @binding(0) var material: CustomMaterial; +const COLOR: vec4 = vec4(0.0, 0.5, 0.0, 0.8); +const POINTINESS: f32 = 2.; +const SPEED: f32 = 3.; +const LENGTH: f32 = 1.; +const SPACING: f32 = 0.5; +const FADE: f32 = 3.; + @fragment fn fragment( #import bevy_pbr::mesh_vertex_output ) -> @location(0) vec4 { let world_space_length: f32 = length(mesh.model[0].xyz); let scaled_x: f32 = uv.x * world_space_length; - let offset_y: f32 = abs(uv.y - 0.5) * material.pointiness; - let scaled_time: f32 = globals.time * material.speed; - let total_length = material.length + material.spacing; + let offset_y: f32 = abs(uv.y - 0.5) * POINTINESS; + let scaled_time: f32 = globals.time * SPEED; + let total_length = LENGTH + SPACING; let value = scaled_x + offset_y - scaled_time; // Ensure that the result of the modulo operation is always positive let positive_modulo = (value % total_length + total_length) % total_length; - let alpha = step(material.spacing, positive_modulo); + let alpha = step(SPACING, positive_modulo); - let start_fade: f32 = (floor(value / total_length) * total_length + scaled_time) / material.fade; - let end_fade: f32 = (world_space_length - ((ceil(value / total_length) * total_length + scaled_time))) / material.fade; + let start_fade: f32 = (floor(value / total_length) * total_length + scaled_time) / FADE; + let end_fade: f32 = (world_space_length - ((ceil(value / total_length) * total_length + scaled_time))) / FADE; let fade = min(1., min(start_fade, end_fade)); - return material.color * vec4(1., 1., 1., alpha * fade); + return COLOR * vec4(1., 1., 1., alpha * fade); } diff --git a/crates/construction/src/manufacturing.rs b/crates/construction/src/manufacturing.rs index a7f12c86..507a3895 100644 --- a/crates/construction/src/manufacturing.rs +++ b/crates/construction/src/manufacturing.rs @@ -15,7 +15,9 @@ use de_core::{ use de_index::SpatialQuery; use de_objects::SolidObjects; use de_pathing::{PathQueryProps, PathTarget, UpdateEntityPath}; -use de_signs::{UpdateLineEndEvent, UpdateLineLocationEvent, UpdatePoleLocationEvent}; +use de_signs::{ + LineLocation, UpdateLineEndEvent, UpdateLineLocationEvent, UpdatePoleLocationEvent, +}; use de_spawner::{ObjectCounter, SpawnBundle}; use parry2d::bounding_volume::Aabb; use parry3d::math::Isometry; @@ -319,7 +321,10 @@ fn configure( let delivery_location = DeliveryLocation::initial(local_aabb, transform); pole_events.send(UpdatePoleLocationEvent::new(entity, delivery_location.0)); let end = delivery_location.0.to_msl(); - line_events.send(UpdateLineLocationEvent::new(entity, start, end)); + line_events.send(UpdateLineLocationEvent::new( + entity, + LineLocation::new(start, end), + )); commands .entity(entity) .insert((AssemblyLine::default(), delivery_location)); diff --git a/crates/signs/src/lib.rs b/crates/signs/src/lib.rs index 9c44a175..219b4b65 100644 --- a/crates/signs/src/lib.rs +++ b/crates/signs/src/lib.rs @@ -2,7 +2,9 @@ use bars::BarsPlugin; pub use bars::{UpdateBarValueEvent, UpdateBarVisibilityEvent}; use bevy::{app::PluginGroupBuilder, prelude::*}; use line::LinePlugin; -pub use line::{UpdateLineEndEvent, UpdateLineLocationEvent, UpdateLineVisibilityEvent}; +pub use line::{ + LineLocation, UpdateLineEndEvent, UpdateLineLocationEvent, UpdateLineVisibilityEvent, +}; use markers::MarkersPlugin; use pole::PolePlugin; pub use pole::{UpdatePoleLocationEvent, UpdatePoleVisibilityEvent}; diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs index 016f0f1e..3b0f6be5 100644 --- a/crates/signs/src/line.rs +++ b/crates/signs/src/line.rs @@ -5,22 +5,13 @@ use bevy::prelude::*; use bevy::reflect::TypeUuid; use bevy::render::render_resource::{AsBindGroup, ShaderRef}; use de_core::baseset::GameSet; +use de_core::cleanup::DespawnOnGameExit; use de_core::state::AppState; /// Width of the line that goes to the pole. const LINE_WIDTH: f32 = 1.; /// Offset above mean sea level of the line, stopping z-fighting with the floor. const LINE_OFFSET: Vec3 = Vec3::new(0., 1e-3, 0.); -/// Material configuration used for the lines to the factory spawn point -const LINE_MATERIAL: LineMaterial = LineMaterial { - color: Color::rgba(0.0, 0.5, 0.0, 0.8), - pointiness: 2., - speed: 3., - length: 1., - spacing: 0.5, - fade: 3., - alpha_mode: AlphaMode::Blend, -}; pub(crate) struct LinePlugin; @@ -30,8 +21,6 @@ impl Plugin for LinePlugin { .add_event::() .add_event::() .add_event::() - .add_event::() - .add_event::() .add_system(setup.in_schedule(OnEnter(AppState::InGame))) .add_system(cleanup.in_schedule(OnExit(AppState::InGame))) .add_system( @@ -57,19 +46,10 @@ impl Plugin for LinePlugin { .in_set(LinesSet::VisibilityEvents), ) .add_system( - spawn_line + owner_despawn .in_base_set(GameSet::PostUpdate) .run_if(in_state(AppState::InGame)) - .run_if(on_event::()) - .in_set(LinesSet::SpawnLines) - .after(LinesSet::VisibilityEvents), - ) - .add_system( - despawn_line - .in_base_set(GameSet::PostUpdate) - .run_if(in_state(AppState::InGame)) - .run_if(on_event::()) - .in_set(LinesSet::SpawnLines) + .in_set(LinesSet::Despawn) .after(LinesSet::VisibilityEvents), ); } @@ -80,23 +60,47 @@ enum LinesSet { LineEnd, LocationEvents, VisibilityEvents, - SpawnLines, + Despawn, } -#[derive(Resource)] -struct LineMesh(Handle); +// Passed to the `rally_point.wgsl` shader +#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[uuid = "d0fae52d-f398-4416-9b72-9039093a6c34"] +pub struct LineMaterial {} -fn setup(mut commands: Commands, mut meshes: ResMut>) { - commands.init_resource::(); - commands.init_resource::(); - let line_mesh = meshes.add(shape::Plane::from_size(1.0).into()); - commands.insert_resource(LineMesh(line_mesh)); +impl Material for LineMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/rally_point.wgsl".into() + } + + fn alpha_mode(&self) -> AlphaMode { + AlphaMode::Blend + } } -fn cleanup(mut commands: Commands) { - commands.remove_resource::(); - commands.remove_resource::(); - commands.remove_resource::(); +#[derive(Clone, Copy)] +pub struct LineLocation { + start: Vec3, + end: Vec3, +} + +impl LineLocation { + pub fn new(start: Vec3, end: Vec3) -> Self { + Self { start, end } + } + + /// A transform matrix from a plane with points at `(-0.5, 0. -0.5),(0.5, 0. -0.5),(0.5, 0. 0.5),(-0.5, 0.-0.5)` + /// to the line start and end with the `LINE_WIDTH`. + fn transform(&self) -> Transform { + let line_direction = self.end - self.start; + let perpendicular_direction = + Vec3::new(-line_direction.z, line_direction.y, line_direction.x).normalize() + * LINE_WIDTH; + let x_axis = line_direction.extend(0.); + let z_axis = perpendicular_direction.extend(0.); + let w_axis = (self.start + line_direction / 2. + LINE_OFFSET).extend(1.); + Transform::from_matrix(Mat4::from_cols(x_axis, Vec4::Y, z_axis, w_axis)) + } } pub struct UpdateLineVisibilityEvent { @@ -112,13 +116,12 @@ impl UpdateLineVisibilityEvent { pub struct UpdateLineLocationEvent { owner: Entity, - start: Vec3, - end: Vec3, + location: LineLocation, } impl UpdateLineLocationEvent { - pub fn new(owner: Entity, start: Vec3, end: Vec3) -> Self { - Self { owner, start, end } + pub fn new(owner: Entity, location: LineLocation) -> Self { + Self { owner, location } } } @@ -133,26 +136,42 @@ impl UpdateLineEndEvent { } } -struct DespawnLineEvent { - owner: Entity, -} - -struct SpawnLineEvent(Transform, Entity); +#[derive(Resource)] +struct LineMesh(Handle, Handle); #[derive(Resource, Default)] struct LineEntities(AHashMap); #[derive(Resource, Default)] -struct LineTransforms(AHashMap); +struct LineLocations(AHashMap); + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + commands.init_resource::(); + commands.init_resource::(); + let line_mesh = meshes.add(shape::Plane::from_size(1.0).into()); + let line_material = materials.add(LineMaterial {}); + commands.insert_resource(LineMesh(line_mesh, line_material)); +} + +fn cleanup(mut commands: Commands) { + commands.remove_resource::(); + commands.remove_resource::(); + commands.remove_resource::(); +} fn update_line_end( mut events: EventReader, - lines: Res, + lines: Res, mut line_location: EventWriter, ) { for event in &mut events { - if let Some([start, _end]) = lines.0.get(&event.owner) { - line_location.send(UpdateLineLocationEvent::new(event.owner, *start, event.end)); + if let Some(old_location) = lines.0.get(&event.owner) { + let location = LineLocation::new(old_location.start, event.end); + line_location.send(UpdateLineLocationEvent::new(event.owner, location)); } } } @@ -161,15 +180,13 @@ fn update_line_location( lines: Res, mut events: EventReader, mut transforms: Query<&mut Transform>, - mut line_transforms: ResMut, + mut line_locations: ResMut, ) { for event in &mut events { - let transform = Transform::from_matrix(compute_line_transform(event.start, event.end)); - let positions = [event.start, event.end]; - line_transforms.0.insert(event.owner, positions); + line_locations.0.insert(event.owner, event.location); if let Some(line_entity) = lines.0.get(&event.owner) { let mut current_transform = transforms.get_mut(*line_entity).unwrap(); - *current_transform = transform + *current_transform = event.location.transform() } } } @@ -177,92 +194,44 @@ fn update_line_location( fn update_line_visibility( mut events: EventReader, mut lines: ResMut, - line_transforms: Res, - mut spawn_line_events: EventWriter, - mut despawn_line_events: EventWriter, + line_locations: Res, + mut commands: Commands, + line_mesh: Res, ) { for event in &mut events { let line_entity = lines.0.entry(event.owner); if event.visible && matches!(line_entity, Entry::Vacant(_)) { - if let Some([start, end]) = line_transforms.0.get(&event.owner) { - let transform = Transform::from_matrix(compute_line_transform(*start, *end)); - spawn_line_events.send(SpawnLineEvent(transform, event.owner)); - } + let transform = line_locations + .0 + .get(&event.owner) + .map(|location| location.transform()); + let line_id = commands + .spawn(( + MaterialMeshBundle { + mesh: line_mesh.0.clone(), + material: line_mesh.1.clone(), + transform: transform.unwrap_or_default(), + ..default() + }, + DespawnOnGameExit, + )) + .id(); + lines.0.insert(event.owner, line_id); } else if !event.visible { - let owner = event.owner; - despawn_line_events.send(DespawnLineEvent { owner }); + if let Some(line_entity) = lines.0.remove(&event.owner) { + commands.entity(line_entity).despawn_recursive(); + } } } } -fn spawn_line( - mut lines: ResMut, - mut events: EventReader, - mut commands: Commands, - line_mesh: Res, - mut materials: ResMut>, -) { - for SpawnLineEvent(transform, owner) in &mut events { - let line_id = commands - .spawn(MaterialMeshBundle { - mesh: line_mesh.0.clone(), - transform: *transform, - material: materials.add(LINE_MATERIAL), - ..default() - }) - .id(); - lines.0.insert(*owner, line_id); - } -} - -fn despawn_line( - mut commands: Commands, - mut events: EventReader, - mut lines: ResMut, -) { - for event in &mut events { - if let Some(line_entity) = lines.0.remove(&event.owner) { - commands.entity(line_entity).despawn_recursive(); +fn owner_despawn(mut commands: Commands, mut lines: ResMut) { + lines.0.retain(|&owner, &mut line| { + if commands.get_entity(owner).is_some() { + return true; } - } -} -/// A transform matrix from a plane with points at `(-0.5, 0. -0.5),(0.5, 0. -0.5),(0.5, 0. 0.5),(-0.5, 0.-0.5)` to the line start and end with the desired width -fn compute_line_transform(line_start: Vec3, end: Vec3) -> Mat4 { - let line_direction = end - line_start; - let perpendicular_direction = - Vec3::new(-line_direction.z, line_direction.y, line_direction.x).normalize() * LINE_WIDTH; - let x_axis = line_direction.extend(0.); - let z_axis = perpendicular_direction.extend(0.); - let w_axis = (line_start + line_direction / 2. + LINE_OFFSET).extend(1.); - Mat4::from_cols(x_axis, Vec4::Y, z_axis, w_axis) -} - -impl Material for LineMaterial { - fn fragment_shader() -> ShaderRef { - "shaders/rally_point.wgsl".into() - } - - fn alpha_mode(&self) -> AlphaMode { - self.alpha_mode - } -} - -// Passed to the `rally_point.wgsl` shader -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] -#[uuid = "d0fae52d-f398-4416-9b72-9039093a6c34"] -pub struct LineMaterial { - #[uniform(0)] - color: Color, - #[uniform(0)] - pointiness: f32, - #[uniform(0)] - speed: f32, - #[uniform(0)] - length: f32, - #[uniform(0)] - spacing: f32, - #[uniform(0)] - fade: f32, - alpha_mode: AlphaMode, + commands.entity(line).despawn(); + false + }); } From c749b549c5783b2e7353dfa9244d6fb87a6afd2c Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Tue, 20 Jun 2023 23:12:22 +0200 Subject: [PATCH 3/7] Fix a doc comment --- crates/signs/src/line.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs index 3b0f6be5..4c7865df 100644 --- a/crates/signs/src/line.rs +++ b/crates/signs/src/line.rs @@ -89,8 +89,8 @@ impl LineLocation { Self { start, end } } - /// A transform matrix from a plane with points at `(-0.5, 0. -0.5),(0.5, 0. -0.5),(0.5, 0. 0.5),(-0.5, 0.-0.5)` - /// to the line start and end with the `LINE_WIDTH`. + /// A transform matrix from a plane with points at `(-0.5, 0. -0.5), (0.5, 0. -0.5), + /// (0.5, 0., 0.5), (-0.5, 0., -0.5)` to the line start and end with the `LINE_WIDTH`. fn transform(&self) -> Transform { let line_direction = self.end - self.start; let perpendicular_direction = From e5372f49ec6be8f56af27887d1c4baf9b9ba3773 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Tue, 20 Jun 2023 23:22:31 +0200 Subject: [PATCH 4/7] Add sysem ordering condition --- crates/signs/src/line.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs index 4c7865df..7c2bb84c 100644 --- a/crates/signs/src/line.rs +++ b/crates/signs/src/line.rs @@ -43,7 +43,8 @@ impl Plugin for LinePlugin { .in_base_set(GameSet::PostUpdate) .run_if(in_state(AppState::InGame)) .run_if(on_event::()) - .in_set(LinesSet::VisibilityEvents), + .in_set(LinesSet::VisibilityEvents) + .after(LinesSet::LocationEvents), ) .add_system( owner_despawn From bdafa8e7e1a23d5097df93e0a8682d9815335316 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Tue, 20 Jun 2023 23:24:14 +0200 Subject: [PATCH 5/7] Cometics --- crates/signs/src/line.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs index 7c2bb84c..bac45d84 100644 --- a/crates/signs/src/line.rs +++ b/crates/signs/src/line.rs @@ -200,8 +200,7 @@ fn update_line_visibility( line_mesh: Res, ) { for event in &mut events { - let line_entity = lines.0.entry(event.owner); - if event.visible && matches!(line_entity, Entry::Vacant(_)) { + if event.visible && !lines.0.contains_key(&event.owner) { let transform = line_locations .0 .get(&event.owner) From 39580e16589d5285c83aa6ebd5b1bb9487ff749f Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Tue, 20 Jun 2023 23:36:21 +0200 Subject: [PATCH 6/7] Remove unused import --- crates/signs/src/line.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs index bac45d84..565520ab 100644 --- a/crates/signs/src/line.rs +++ b/crates/signs/src/line.rs @@ -1,5 +1,3 @@ -use std::collections::hash_map::Entry; - use ahash::AHashMap; use bevy::prelude::*; use bevy::reflect::TypeUuid; From 1d8634c5175e6327550ec0e9e07927e900ace474 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Tue, 20 Jun 2023 23:37:48 +0200 Subject: [PATCH 7/7] Fix & improve owner de-spawning This will be faster in cases with many lines because it does not iterate over all of them. Furthermore, this changes despawn() to despawn_recursive() so it is more future proof and consistent with the other despawn in the module. --- crates/signs/src/line.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/signs/src/line.rs b/crates/signs/src/line.rs index 565520ab..7cc9d8fe 100644 --- a/crates/signs/src/line.rs +++ b/crates/signs/src/line.rs @@ -4,6 +4,7 @@ use bevy::reflect::TypeUuid; use bevy::render::render_resource::{AsBindGroup, ShaderRef}; use de_core::baseset::GameSet; use de_core::cleanup::DespawnOnGameExit; +use de_core::objects::Active; use de_core::state::AppState; /// Width of the line that goes to the pole. @@ -223,13 +224,14 @@ fn update_line_visibility( } } -fn owner_despawn(mut commands: Commands, mut lines: ResMut) { - lines.0.retain(|&owner, &mut line| { - if commands.get_entity(owner).is_some() { - return true; +fn owner_despawn( + mut commands: Commands, + mut lines: ResMut, + mut removed: RemovedComponents, +) { + for owner in removed.iter() { + if let Some(line) = lines.0.remove(&owner) { + commands.entity(line).despawn_recursive(); } - - commands.entity(line).despawn(); - false - }); + } }