Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement some bulding construction prerequisites #140

Merged
merged 6 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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