Skip to content

Commit

Permalink
Implement markers of selected movable entities
Browse files Browse the repository at this point in the history
  • Loading branch information
Indy2222 committed Oct 21, 2022
1 parent fca8b0b commit 3d936cc
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 15 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 48 additions & 2 deletions assets/shaders/terrain.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,25 @@

// How large (in meters) is a texture.
let TEXTURE_SIZE = 16.;
let SHAPE_COLOR = vec4<f32>(1., 1., 1., 0.75);
let SHAPE_THICKNESS = 0.15;

struct Circle {
@align(16) center: vec2<f32>,
radius: f32,
};

struct Circles {
count: i32,
// Keep thie array lenght in sync with /crates/terrain/src/shader.rs.
@align(16) circles: array<Circle, 32>,
};

@group(1) @binding(0)
var terrain_texture: texture_2d<f32>;
var<uniform> circles: Circles;
@group(1) @binding(1)
var terrain_texture: texture_2d<f32>;
@group(1) @binding(2)
var terrain_sampler: sampler;

struct FragmentInput {
Expand All @@ -22,6 +37,35 @@ struct FragmentInput {
#import bevy_pbr::mesh_vertex_output
};

fn mix_colors(base: vec4<f32>, cover: vec4<f32>) -> vec4<f32> {
let alpha = base.a * cover.a;
let rgb = base.rgb * cover.a + cover.rgb * (1. - cover.a);
return vec4<f32>(rgb, alpha);
}

fn draw_circle(
base: vec4<f32>,
uv: vec2<f32>,
center: vec2<f32>,
radius: f32,
) -> vec4<f32> {
let distance: f32 = distance(uv, center);
if distance <= (radius + SHAPE_THICKNESS) && radius <= distance {
return mix_colors(base, SHAPE_COLOR);
}
return base;
}

fn draw_circles(base: vec4<f32>, uv: vec2<f32>) -> vec4<f32> {
var output_color = base;
for ( var i: i32 = 0; i < circles.count; i++ ) {
let center: vec2<f32> = circles.circles[i].center;
let radius = circles.circles[i].radius;
output_color = draw_circle(output_color, uv, center, radius);
}
return output_color;
}

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
var pbr_input: PbrInput = pbr_input_new();
Expand Down Expand Up @@ -58,5 +102,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
);
pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic);

return tone_mapping(pbr(pbr_input));
var output_color = tone_mapping(pbr(pbr_input));
output_color = draw_circles(output_color, in.uv);
return output_color;
}
1 change: 1 addition & 0 deletions crates/controller/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ categories = ["games"]
[dependencies]
# DE
de_core = { path = "../core", version = "0.1.0-dev" }
de_objects = { path = "../objects", version = "0.1.0-dev" }
de_index = { path = "../index", version = "0.1.0-dev" }
de_terrain = { path = "../terrain", version = "0.1.0-dev" }
de_pathing = { path = "../pathing", version = "0.1.0-dev" }
Expand Down
26 changes: 20 additions & 6 deletions crates/controller/src/selection.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use ahash::AHashSet;
use bevy::{
ecs::system::SystemParam,
prelude::{App, Commands, Component, Entity, EventReader, Plugin, Query, With},
use bevy::{ecs::system::SystemParam, prelude::*};
use de_core::{
objects::{MovableSolid, ObjectType},
stages::GameStage,
state::GameState,
};
use de_core::{stages::GameStage, state::GameState};
use de_objects::{IchnographyCache, ObjectCache};
use de_terrain::CircleMarker;
use iyes_loopless::prelude::*;

use crate::Labels;
Expand Down Expand Up @@ -68,7 +71,9 @@ pub(crate) enum SelectionMode {
#[derive(SystemParam)]
struct Selector<'w, 's> {
commands: Commands<'w, 's>,
cache: Res<'w, ObjectCache>,
selected: Query<'w, 's, Entity, With<Selected>>,
movable: Query<'w, 's, &'static ObjectType, With<MovableSolid>>,
}

impl<'w, 's> Selector<'w, 's> {
Expand All @@ -82,11 +87,20 @@ impl<'w, 's> Selector<'w, 's> {
};

for entity in deselect {
self.commands.entity(entity).remove::<Selected>();
let mut entity_commands = self.commands.entity(entity);
entity_commands.remove::<Selected>();
if self.movable.contains(entity) {
entity_commands.remove::<CircleMarker>();
}
}

for entity in select {
self.commands.entity(entity).insert(Selected);
let mut entity_commands = self.commands.entity(entity);
entity_commands.insert(Selected);
if let Ok(&object_type) = self.movable.get(entity) {
let radius = self.cache.get_ichnography(object_type).radius();
entity_commands.insert(CircleMarker::new(radius));
}
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions crates/terrain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,20 @@ impl PluginGroup for TerrainPluginGroup {
group.add(TerrainPlugin);
}
}

/// A semi-transparent circle is drawn on the terrain surface below every
/// entity with this component.
#[derive(Component)]
pub struct CircleMarker {
radius: f32,
}

impl CircleMarker {
pub fn new(radius: f32) -> Self {
Self { radius }
}

pub(crate) fn radius(&self) -> f32 {
self.radius
}
}
54 changes: 51 additions & 3 deletions crates/terrain/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use ahash::AHashSet;
use bevy::{
asset::LoadState,
prelude::*,
render::{
render_resource::{AddressMode, SamplerDescriptor},
texture::ImageSampler,
view::{VisibilitySystems, VisibleEntities},
},
};
use de_core::{stages::GameStage, state::GameState};
use de_core::{projection::ToFlat, stages::GameStage, state::GameState};
use iyes_loopless::prelude::*;
use iyes_progress::prelude::*;

use crate::{shader::TerrainMaterial, terrain::Terrain};
use crate::{shader::TerrainMaterial, terrain::Terrain, CircleMarker};

const TERRAIN_TEXTURE: &str = "textures/terrain.png";

Expand All @@ -25,7 +27,11 @@ impl Plugin for TerrainPlugin {
.track_progress()
.run_in_state(GameState::Loading),
)
.add_system_to_stage(GameStage::Update, init);
.add_system_to_stage(GameStage::Update, init)
.add_system_to_stage(
CoreStage::PostUpdate,
circles.after(VisibilitySystems::CheckVisibility),
);
}
}

Expand Down Expand Up @@ -84,3 +90,45 @@ fn init(
});
}
}

fn circles(
mut materials: ResMut<Assets<TerrainMaterial>>,
camera: Query<&VisibleEntities, With<Camera3d>>,
terrains: Query<(Entity, &ComputedVisibility, &Handle<TerrainMaterial>)>,
circles: Query<(Entity, &ComputedVisibility, &Transform, &CircleMarker)>,
) {
let visible = camera.get_single().map_or_else(
|_| AHashSet::new(),
|v| AHashSet::from_iter(v.iter().cloned()),
);

for (terrain_entity, terrain_visibility, material) in terrains.iter() {
if !terrain_visibility.is_visible_in_hierarchy() {
println!("x");
continue;
}
if !visible.contains(&terrain_entity) {
println!("y");
continue;
}

let material = materials.get_mut(material).unwrap();
material.clear();

for (circle_entity, circle_visibility, transform, marker) in circles.iter() {
if material.is_full() {
break;
}
if !circle_visibility.is_visible_in_hierarchy() {
// it looks like that this does not filter out anything.
println!("aa");
continue;
}
if !visible.contains(&circle_entity) {
println!("ss");
continue;
}
material.push(transform.translation.to_flat(), marker.radius());
}
}
}
58 changes: 54 additions & 4 deletions crates/terrain/src/shader.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
use bevy::{
prelude::{Handle, Image, Material},
reflect::TypeUuid,
render::render_resource::{AsBindGroup, ShaderRef},
render::render_resource::{AsBindGroup, ShaderRef, ShaderType},
};
use glam::Vec2;

// Keep this in sync with terrain.wgsl.
const CIRCLE_CAPACITY: usize = 32;

#[derive(AsBindGroup, TypeUuid, Debug, Clone)]
#[uuid = "9e124e04-fdf1-4836-b82d-fa2f01fddb62"]
pub struct TerrainMaterial {
#[texture(0)]
#[sampler(1)]
#[uniform(0)]
circles: Circles,
#[texture(1)]
#[sampler(2)]
texture: Handle<Image>,
}

impl TerrainMaterial {
pub(crate) fn new(texture: Handle<Image>) -> Self {
Self { texture }
Self {
circles: Circles::default(),
texture,
}
}

pub(crate) fn is_full(&self) -> bool {
self.circles.is_full()
}

pub(crate) fn clear(&mut self) {
self.circles.clear();
}

pub(crate) fn push(&mut self, center: Vec2, radius: f32) {
self.circles.push(center, radius);
}
}

Expand All @@ -23,3 +44,32 @@ impl Material for TerrainMaterial {
"shaders/terrain.wgsl".into()
}
}

#[derive(ShaderType, Debug, Clone, Default)]
struct Circles {
#[size(16)]
count: i32,
circles: [Circle; CIRCLE_CAPACITY],
}

impl Circles {
pub(crate) fn is_full(&self) -> bool {
self.count as usize >= CIRCLE_CAPACITY
}

fn clear(&mut self) {
self.count = 0;
}

fn push(&mut self, center: Vec2, radius: f32) {
self.circles[self.count as usize] = Circle { center, radius };
self.count += 1;
}
}

#[derive(ShaderType, Debug, Clone, Copy, Default)]
struct Circle {
#[align(16)]
center: Vec2,
radius: f32,
}

0 comments on commit 3d936cc

Please sign in to comment.