Skip to content

Commit

Permalink
Merge pull request #140 from DigitalExtinction/feature/improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Indy2222 authored Jun 27, 2022
2 parents e3d447f + 5fa826d commit 38acb06
Show file tree
Hide file tree
Showing 16 changed files with 492 additions and 267 deletions.
71 changes: 53 additions & 18 deletions crates/controller/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,57 @@
use bevy::{
input::mouse::MouseButtonInput,
prelude::{
App, Entity, EventReader, EventWriter, MouseButton, ParallelSystemDescriptorCoercion,
Plugin, Query, Res, SystemSet, With,
},
input::{mouse::MouseButtonInput, ElementState},
prelude::*,
};
use de_core::{objects::MovableSolid, projection::ToFlat};
use de_pathing::UpdateEntityPath;
use iyes_loopless::prelude::*;

use crate::{pointer::Pointer, selection::Selected, Labels};
use crate::{
pointer::Pointer,
selection::{SelectEvent, Selected, SelectionMode},
Labels,
};

pub(crate) struct CommandPlugin;

impl Plugin for CommandPlugin {
fn build(&self, app: &mut App) {
app.add_system_set(
SystemSet::new().with_system(
mouse_click_handler
.label(Labels::InputUpdate)
.after(Labels::PreInputUpdate),
),
app.add_system_set_to_stage(
CoreStage::PreUpdate,
SystemSet::new()
.with_system(
right_click_handler
.run_if(on_pressed(MouseButton::Right))
.label(Labels::InputUpdate)
.after(Labels::PreInputUpdate),
)
.with_system(
left_click_handler
.run_if(on_pressed(MouseButton::Left))
.label(Labels::InputUpdate)
.after(Labels::PreInputUpdate),
),
);
}
}

fn mouse_click_handler(
mut click_events: EventReader<MouseButtonInput>,
fn on_pressed(button: MouseButton) -> impl Fn(EventReader<MouseButtonInput>) -> bool {
move |mut events: EventReader<MouseButtonInput>| {
// It is desirable to exhaust the iterator, thus .filter().count() is
// used instead of .any()
events
.iter()
.filter(|e| e.button == button && e.state == ElementState::Pressed)
.count()
> 0
}
}

fn right_click_handler(
mut path_events: EventWriter<UpdateEntityPath>,
selected: Query<Entity, (With<Selected>, With<MovableSolid>)>,
pointer: Res<Pointer>,
) {
if !click_events.iter().any(|e| e.button == MouseButton::Right) {
return;
}

let target = match pointer.terrain_point() {
Some(point) => point.to_flat(),
None => return,
Expand All @@ -43,3 +61,20 @@ fn mouse_click_handler(
path_events.send(UpdateEntityPath::new(entity, target));
}
}

fn left_click_handler(
mut events: EventWriter<SelectEvent>,
keys: Res<Input<KeyCode>>,
pointer: Res<Pointer>,
) {
let selection_mode = if keys.pressed(KeyCode::LControl) {
SelectionMode::Add
} else {
SelectionMode::Replace
};
let event = match pointer.entity() {
Some(entity) => SelectEvent::single(entity, selection_mode),
None => SelectEvent::none(selection_mode),
};
events.send(event);
}
10 changes: 3 additions & 7 deletions crates/controller/src/pointer.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use bevy::{
ecs::system::SystemParam,
input::mouse::MouseMotion,
prelude::{
App, Camera, Entity, EventReader, GlobalTransform, Plugin, Query, Res, ResMut, With,
},
render::camera::Camera3d,
ecs::system::SystemParam, input::mouse::MouseMotion, prelude::*, render::camera::Camera3d,
window::Windows,
};
use de_core::{objects::Playable, state::GameState};
Expand All @@ -20,7 +15,8 @@ pub(crate) struct PointerPlugin;

impl Plugin for PointerPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Pointer>().add_system(
app.init_resource::<Pointer>().add_system_to_stage(
CoreStage::PreUpdate,
mouse_move_handler
.run_in_state(GameState::Playing)
.label(Labels::PreInputUpdate),
Expand Down
77 changes: 39 additions & 38 deletions crates/controller/src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,60 @@ use std::collections::HashSet;

use bevy::{
ecs::system::SystemParam,
input::{mouse::MouseButtonInput, ElementState, Input},
prelude::{
App, Commands, Component, Entity, EventReader, KeyCode, MouseButton, Plugin, Query, Res,
With,
},
prelude::{App, Commands, Component, CoreStage, Entity, EventReader, Plugin, Query, With},
};
use de_core::state::GameState;
use iyes_loopless::prelude::*;

use crate::{pointer::Pointer, Labels};
use crate::Labels;

pub(crate) struct SelectionPlugin;

impl Plugin for SelectionPlugin {
fn build(&self, app: &mut App) {
app.add_system(
mouse_click_handler
app.add_event::<SelectEvent>().add_system_to_stage(
CoreStage::PreUpdate,
update_selection
.run_in_state(GameState::Playing)
.label(Labels::InputUpdate)
.after(Labels::PreInputUpdate),
.after(Labels::InputUpdate),
);
}
}

pub(crate) struct SelectEvent {
entities: Vec<Entity>,
mode: SelectionMode,
}

impl SelectEvent {
pub(crate) fn none(mode: SelectionMode) -> Self {
Self {
entities: Vec::new(),
mode,
}
}

pub(crate) fn single(entity: Entity, mode: SelectionMode) -> Self {
Self {
entities: vec![entity],
mode,
}
}

fn entities(&self) -> &[Entity] {
self.entities.as_slice()
}

fn mode(&self) -> SelectionMode {
self.mode
}
}

#[derive(Component)]
pub(crate) struct Selected;

#[derive(Clone, Copy, PartialEq)]
enum SelectionMode {
pub(crate) enum SelectionMode {
Replace,
Add,
}
Expand All @@ -42,14 +67,6 @@ struct Selector<'w, 's> {
}

impl<'w, 's> Selector<'w, 's> {
fn select_single(&mut self, entity: Option<Entity>, mode: SelectionMode) {
let entities = match entity {
Some(entity) => vec![entity],
None => Vec::new(),
};
self.select(&entities, mode);
}

fn select(&mut self, entities: &[Entity], mode: SelectionMode) {
let selected: HashSet<Entity> = self.selected.iter().collect();
let desired: HashSet<Entity> = entities.iter().cloned().collect();
Expand All @@ -65,24 +82,8 @@ impl<'w, 's> Selector<'w, 's> {
}
}

fn mouse_click_handler(
mut event: EventReader<MouseButtonInput>,
keys: Res<Input<KeyCode>>,
pointer: Res<Pointer>,
mut selector: Selector,
) {
if !event
.iter()
.any(|e| e.button == MouseButton::Left && e.state == ElementState::Pressed)
{
return;
fn update_selection(mut events: EventReader<SelectEvent>, mut selector: Selector) {
for event in events.iter() {
selector.select(event.entities(), event.mode());
}

let mode = if keys.pressed(KeyCode::LControl) {
SelectionMode::Add
} else {
SelectionMode::Replace
};

selector.select_single(pointer.entity(), mode);
}
9 changes: 9 additions & 0 deletions crates/core/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ pub enum ObjectType {
Inactive(InactiveObjectType),
}

impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Active(active) => write!(f, "Active -> {}", active),
Self::Inactive(inactive) => write!(f, "Inactive -> {}", inactive),
}
}
}

#[derive(Copy, Clone, Debug, Component, Serialize, Deserialize, PartialEq, Enum)]
pub enum InactiveObjectType {
Tree,
Expand Down
10 changes: 6 additions & 4 deletions crates/index/benches/ray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use criterion::{
criterion_group, criterion_main, AxisScale, BenchmarkId, Criterion, PlotConfiguration,
Throughput,
};
use de_index::{EntityIndex, SpatialQuery};
use de_index::{EntityIndex, LocalCollider, SpatialQuery};
use de_objects::ObjectCollider;
use glam::Vec2;
use parry3d::{
Expand Down Expand Up @@ -77,9 +77,11 @@ fn setup_world(num_entities: u32, max_distance: f32) -> World {
let mut index = EntityIndex::new();

for (i, point) in points.iter().enumerate() {
let collider = ObjectCollider::new(Cuboid::new(Vector::new(3., 3., 4.)).into());
let position = Isometry::new(Vector::new(point.x, 0., point.y), Vector::identity());
index.insert(Entity::from_raw(i as u32), collider, position);
let collider = LocalCollider::new(
ObjectCollider::new(Cuboid::new(Vector::new(3., 3., 4.)).into()),
Isometry::new(Vector::new(point.x, 0., point.y), Vector::identity()),
);
index.insert(Entity::from_raw(i as u32), collider);
}

let mut rays = Rays::new();
Expand Down
110 changes: 110 additions & 0 deletions crates/index/src/aabb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use ahash::AHashSet;
use bevy::prelude::Entity;
use parry3d::bounding_volume::AABB;

use crate::{grid::TileGrid, range::TileRange};

/// An iterator over unique entity IDs withing a box.
pub(crate) struct AabbCandidates<'a> {
grid: &'a TileGrid,
tiles: TileRange,
row: Option<i32>,
prev_row: AHashSet<Entity>,
current_row: AHashSet<Entity>,
}

impl<'a> AabbCandidates<'a> {
/// Creates a new iterator of entities potentially colliding with a given
/// AABB.
pub(crate) fn new(grid: &'a TileGrid, aabb: &AABB) -> Self {
Self {
grid,
tiles: TileRange::from_aabb(aabb),
row: None,
prev_row: AHashSet::new(),
current_row: AHashSet::new(),
}
}
}

impl<'a> Iterator for AabbCandidates<'a> {
type Item = AHashSet<Entity>;

fn next(&mut self) -> Option<AHashSet<Entity>> {
loop {
let tile_coords = match self.tiles.next() {
Some(tile_coords) => tile_coords,
None => return None,
};

let row = Some(tile_coords.y);
if self.row != row {
std::mem::swap(&mut self.prev_row, &mut self.current_row);
self.current_row.clear();
self.row = row;
}

if let Some(entities) = self.grid.get_tile_entities(tile_coords) {
debug_assert!(!entities.is_empty());

let mut new_entities = entities.to_owned();
for entity in self.current_row.iter() {
new_entities.remove(entity);
}
self.current_row.extend(&new_entities);
for entity in self.prev_row.iter() {
new_entities.remove(entity);
}

if !new_entities.is_empty() {
return Some(new_entities);
}
}
}
}
}

#[cfg(test)]
mod tests {
use parry3d::math::Point;

use super::*;
use crate::TILE_SIZE;

#[test]
fn test_aabb() {
let entity_a = Entity::from_raw(1);
let aabb_a = AABB::new(
Point::new(0.5 * TILE_SIZE, 0., 1.1 * TILE_SIZE),
Point::new(3.7 * TILE_SIZE, 3., 1.6 * TILE_SIZE),
);
let entity_b = Entity::from_raw(2);
let aabb_b = AABB::new(
Point::new(-TILE_SIZE * 0.7, -100.5, -TILE_SIZE * 3.5),
Point::new(-TILE_SIZE * 0.6, 3.5, -TILE_SIZE * 3.2),
);
let entity_c = Entity::from_raw(3);
let aabb_c = AABB::new(
Point::new(TILE_SIZE * 20.1, 0.5, TILE_SIZE * 20.5),
Point::new(TILE_SIZE * 20., 0.5, TILE_SIZE * 20.2),
);

let mut grid = TileGrid::new();
grid.insert(entity_a, &aabb_a);
grid.insert(entity_b, &aabb_b);
grid.insert(entity_c, &aabb_c);

let mut candidates = AabbCandidates::new(
&grid,
&AABB::new(
Point::new(-TILE_SIZE * 0.1, 0.5, -TILE_SIZE * 5.5),
Point::new(TILE_SIZE * 0.9, 0.5, TILE_SIZE * 1.2),
),
);
let first = candidates.next().unwrap();
assert_eq!(first, AHashSet::from_iter(vec![entity_a]));
let second = candidates.next().unwrap();
assert_eq!(second, AHashSet::from_iter(vec![entity_b]));
assert!(candidates.next().is_none());
}
}
Loading

0 comments on commit 38acb06

Please sign in to comment.