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 22, 2022
1 parent ff2b06e commit 07659a9
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 19 deletions.
6 changes: 4 additions & 2 deletions Cargo.lock

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

126 changes: 124 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 KdTreeNode {
@align(16) location: vec2<f32>,
radius: f32,
};

struct KdTree {
// Keep thie array lenght in sync with /crates/terrain/src/shader.rs.
@align(16) nodes: array<KdTreeNode, 1023>,
count: u32,
};

@group(1) @binding(0)
var terrain_texture: texture_2d<f32>;
var<uniform> circles: KdTree;
@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,111 @@ 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;
}

struct KdRecord {
index: u32,
distance: f32,
}

struct Next {
index: u32,
depth: u32,
potential: f32,
}

fn nearest(uv: vec2<f32>) -> u32 {
if circles.count == 0u {
return 1023u; // TODO constant
}

var best: KdRecord;
best.index = 0u;
best.distance = distance(circles.nodes[0].location, uv);

var stack_size: u32 = 1u;
var stack: array<Next, 18>;
stack[0].index = 0u;
stack[0].potential = 0.;
stack[0].depth = 0u;

while stack_size > 0u {
stack_size -= 1u;
let next = stack[stack_size];

if next.potential >= best.distance {
continue;
}

let node = circles.nodes[next.index];

let distance = distance(node.location, uv);
if distance < best.distance {
best.index = next.index;
best.distance = distance;
}

let axis = next.depth % 2u;
let diff = uv[axis] - node.location[axis];

var close = 2u * next.index + 2u;
var away = 2u * next.index + 1u;

if diff <= 0. { // TODO check this
close -= 1u;
away += 1u;
}


if away < circles.count {
stack[stack_size].index = away;
stack[stack_size].potential = abs(diff);
stack[stack_size].depth = next.depth + 1u;
stack_size += 1u;
}

if close < circles.count {
stack[stack_size].index = close;
stack[stack_size].potential = 0.;
stack[stack_size].depth = next.depth + 1u;
stack_size += 1u;
}
}

return best.index;
}

fn draw_circles(base: vec4<f32>, uv: vec2<f32>) -> vec4<f32> {
var output_color = base;

let index = nearest(uv);
if index < 1023u { // TODO constant
let node = circles.nodes[index];
let center = node.location;
let radius = node.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 +178,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
2 changes: 1 addition & 1 deletion crates/core/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
/// Maximum number of buildings belonging to a single player.
pub const PLAYER_MAX_BUILDINGS: usize = 128;
/// Maximum number of units belonging to a single player.
pub const PLAYER_MAX_UNITS: usize = 1024;
pub const PLAYER_MAX_UNITS: usize = 1023;

/// Active object which can be played by the local player.
#[derive(Component)]
Expand Down
3 changes: 3 additions & 0 deletions crates/terrain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ iyes_progress = { version = "0.4", features = [ "iyes_loopless" ] }
glam = "0.21"
parry3d = "0.9.0"
ahash = "0.7.6"

[dev-dependencies]
itertools = "0.10.5"
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
}
}
43 changes: 40 additions & 3 deletions crates/terrain/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ use bevy::{
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::{Circle, TerrainMaterial},
terrain::Terrain,
CircleMarker,
};

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

Expand All @@ -25,7 +30,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 +93,31 @@ fn init(
});
}
}

fn circles(
mut materials: ResMut<Assets<TerrainMaterial>>,
camera: Query<&VisibleEntities, With<Camera3d>>,
terrains: Query<(Entity, &ComputedVisibility, &Handle<TerrainMaterial>)>,
markers: Query<(Entity, &ComputedVisibility, &Transform, &CircleMarker)>,
) {
for (terrain_entity, terrain_visibility, material) in terrains.iter() {
if !terrain_visibility.is_visible_in_hierarchy() {
continue;
}

let mut circles = Vec::new();
for (circle_entity, circle_visibility, transform, marker) in markers.iter() {
// TODO real frustum filtering
if !circle_visibility.is_visible_in_hierarchy() {
continue;
}
circles.push(Circle::new(
transform.translation.to_flat(),
marker.radius(),
));
}

let material = materials.get_mut(material).unwrap();
material.set_markers(circles);
}
}
Loading

0 comments on commit 07659a9

Please sign in to comment.