Skip to content

Commit 2d4e00c

Browse files
committed
de_index: Prep for faster indexing alternative
1 parent 4b89fc3 commit 2d4e00c

File tree

9 files changed

+65
-63
lines changed

9 files changed

+65
-63
lines changed

crates/index/src/lib.rs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
#![allow(rustdoc::private_intra_doc_links)]
2-
//! This module implements 2D object partitioning for fast geometric lookup,
3-
//! for example ray casting.
4-
//!
5-
//! The core structure is a square tile grid which points to Bevy ECS entities.
6-
//! Newly spawned entities are automatically added, despawned entities removed
7-
//! and moved entities updated by systems added by
8-
//! [`self::IndexPlugin`].
9-
mod aabb;
10-
mod collider;
11-
mod grid;
12-
mod index;
13-
mod range;
14-
mod segment;
15-
mod systems;
2+
//! This crate implements spatial indexing and various spatial queries of game
3+
//! entities.
164
17-
use bevy::{app::PluginGroupBuilder, prelude::PluginGroup};
18-
use systems::IndexPlugin;
5+
mod precise;
196

20-
pub use self::{
21-
collider::{ColliderWithCache, LocalCollider, QueryCollider},
22-
index::{EntityIndex, RayEntityIntersection, SpatialQuery},
23-
systems::IndexSet,
7+
use bevy::{app::PluginGroupBuilder, prelude::PluginGroup};
8+
use precise::PreciseIndexPlugin;
9+
pub use precise::{
10+
ColliderWithCache, EntityIndex, LocalCollider, PreciseIndexSet, QueryCollider,
11+
RayEntityIntersection, SpatialQuery,
2412
};
2513

2614
/// Size (in world-space) of a single square tile where entities are kept.
@@ -30,6 +18,6 @@ pub struct IndexPluginGroup;
3018

3119
impl PluginGroup for IndexPluginGroup {
3220
fn build(self) -> PluginGroupBuilder {
33-
PluginGroupBuilder::start::<Self>().add(IndexPlugin)
21+
PluginGroupBuilder::start::<Self>().add(PreciseIndexPlugin)
3422
}
3523
}

crates/index/src/aabb.rs renamed to crates/index/src/precise/aabb.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use ahash::AHashSet;
22
use bevy::prelude::Entity;
33
use parry3d::bounding_volume::Aabb;
44

5-
use crate::{grid::TileGrid, range::TileRange};
5+
use super::{grid::TileGrid, range::TileRange};
66

77
/// An iterator over unique entity IDs withing a box.
8-
pub(crate) struct AabbCandidates<'a> {
8+
pub(super) struct AabbCandidates<'a> {
99
grid: &'a TileGrid,
1010
tiles: TileRange,
1111
row: Option<i32>,
@@ -16,7 +16,7 @@ pub(crate) struct AabbCandidates<'a> {
1616
impl<'a> AabbCandidates<'a> {
1717
/// Creates a new iterator of entities potentially colliding with a given
1818
/// AABB.
19-
pub(crate) fn new(grid: &'a TileGrid, aabb: &Aabb) -> Self {
19+
pub(super) fn new(grid: &'a TileGrid, aabb: &Aabb) -> Self {
2020
Self {
2121
grid,
2222
tiles: TileRange::from_aabb(aabb),

crates/index/src/collider.rs renamed to crates/index/src/precise/collider.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,20 @@ impl LocalCollider {
4343
}
4444

4545
/// Updates position of cached world-space AABB of the collider.
46-
pub(crate) fn update_position(&mut self, position: Isometry<f32>) {
46+
pub(super) fn update_position(&mut self, position: Isometry<f32>) {
4747
self.world_aabb = self.local_aabb.transform_by(&position);
4848
self.position = position;
4949
}
5050

51-
pub(crate) fn cast_ray(&self, ray: &Ray, max_toi: f32) -> Option<f32> {
51+
pub(super) fn cast_ray(&self, ray: &Ray, max_toi: f32) -> Option<f32> {
5252
if self.world_aabb.intersects_local_ray(ray, max_toi) {
5353
self.object_collider.cast_ray(&self.position, ray, max_toi)
5454
} else {
5555
None
5656
}
5757
}
5858

59-
pub(crate) fn intersects(&self, rhs: &impl ColliderWithCache) -> bool {
59+
pub(super) fn intersects(&self, rhs: &impl ColliderWithCache) -> bool {
6060
if self.query_aabb(rhs.world_aabb()) {
6161
self.object_collider
6262
.intersects(&self.position, rhs.inner(), rhs.position())
@@ -67,7 +67,7 @@ impl LocalCollider {
6767

6868
/// Returns true if world-space axis-aligned bounding boxes of the two
6969
/// colliders intersect.
70-
pub(crate) fn query_aabb(&self, aabb: &Aabb) -> bool {
70+
pub(super) fn query_aabb(&self, aabb: &Aabb) -> bool {
7171
self.world_aabb.intersects(aabb)
7272
}
7373
}

crates/index/src/grid.rs renamed to crates/index/src/precise/grid.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ use bevy::prelude::Entity;
66
use glam::IVec2;
77
use parry3d::bounding_volume::Aabb;
88

9-
use crate::range::TileRange;
9+
use super::range::TileRange;
1010

1111
/// Rectangular (2D) grid of sets of Bevy ECS entities.
1212
///
1313
/// Only non-empty sets are kept (a hash map mapping 2D tile coordinates to
1414
/// Entity sets is used under the hood). Each set contains entities whose
1515
/// absolute AABB intersects with the tile.
16-
pub(crate) struct TileGrid {
16+
pub(super) struct TileGrid {
1717
tiles: AHashMap<IVec2, AHashSet<Entity>>,
1818
}
1919

2020
impl TileGrid {
2121
/// Creates a new empty grid.
22-
pub(crate) fn new() -> Self {
22+
pub(super) fn new() -> Self {
2323
Self {
2424
tiles: AHashMap::new(),
2525
}
@@ -36,7 +36,7 @@ impl TileGrid {
3636
/// # Panics
3737
///
3838
/// Might panic if the entity is already present in the grid.
39-
pub(crate) fn insert(&mut self, entity: Entity, aabb: &Aabb) {
39+
pub(super) fn insert(&mut self, entity: Entity, aabb: &Aabb) {
4040
for tile in TileRange::from_aabb(aabb) {
4141
self.insert_to_tile(entity, tile);
4242
}
@@ -56,7 +56,7 @@ impl TileGrid {
5656
///
5757
/// Might panic if the entity is not stored in the grid or if the last used
5858
/// update / insertion AABB differs from the one passed as an argument.
59-
pub(crate) fn remove(&mut self, entity: Entity, aabb: &Aabb) {
59+
pub(super) fn remove(&mut self, entity: Entity, aabb: &Aabb) {
6060
for tile in TileRange::from_aabb(aabb) {
6161
self.remove_from_tile(entity, tile);
6262
}
@@ -77,7 +77,7 @@ impl TileGrid {
7777
///
7878
/// Might panic if the entity is not present in the grid or if `old_aabb`
7979
/// differs from the last used update / insert AABB.
80-
pub(crate) fn update(&mut self, entity: Entity, old_aabb: &Aabb, new_aabb: &Aabb) {
80+
pub(super) fn update(&mut self, entity: Entity, old_aabb: &Aabb, new_aabb: &Aabb) {
8181
let old_tiles = TileRange::from_aabb(old_aabb);
8282
let new_tiles = TileRange::from_aabb(new_aabb);
8383

@@ -107,7 +107,7 @@ impl TileGrid {
107107
/// # Arguments
108108
///
109109
/// `tile_coords` - coordinates of the tile.
110-
pub(crate) fn get_tile_entities(&self, tile_coords: IVec2) -> Option<&AHashSet<Entity>> {
110+
pub(super) fn get_tile_entities(&self, tile_coords: IVec2) -> Option<&AHashSet<Entity>> {
111111
self.tiles.get(&tile_coords)
112112
}
113113

crates/index/src/index.rs renamed to crates/index/src/precise/index.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ use parry3d::{
1818
shape::Segment,
1919
};
2020

21-
use super::{collider::LocalCollider, grid::TileGrid, segment::SegmentCandidates};
22-
use crate::{aabb::AabbCandidates, collider::ColliderWithCache};
21+
use super::{
22+
aabb::AabbCandidates, collider::ColliderWithCache, collider::LocalCollider, grid::TileGrid,
23+
segment::SegmentCandidates,
24+
};
2325

2426
/// 2D rectangular grid based spatial index of entities.
2527
#[derive(Resource)]
@@ -47,15 +49,15 @@ impl EntityIndex {
4749
self.colliders.insert(entity, collider);
4850
}
4951

50-
pub(crate) fn remove(&mut self, entity: Entity) {
52+
pub(super) fn remove(&mut self, entity: Entity) {
5153
let collider = self
5254
.colliders
5355
.remove(&entity)
5456
.expect("Tried to remove non-existent entity.");
5557
self.grid.remove(entity, collider.world_aabb());
5658
}
5759

58-
pub(crate) fn update(&mut self, entity: Entity, position: Isometry<f32>) {
60+
pub(super) fn update(&mut self, entity: Entity, position: Isometry<f32>) {
5961
let collider = self
6062
.colliders
6163
.get_mut(&entity)
@@ -107,7 +109,7 @@ impl Default for EntityIndex {
107109
/// System parameter implementing various spatial queries.
108110
///
109111
/// Only entities automatically indexed by systems from
110-
/// [`super::systems::IndexPlugin`] could be retrieved.
112+
/// [`super::PreciseIndexPlugin`] could be retrieved.
111113
#[derive(SystemParam)]
112114
pub struct SpatialQuery<'w, 's, Q, F = ()>
113115
where
@@ -124,7 +126,7 @@ where
124126
F: ReadOnlyWorldQuery + Sync + Send + 'static,
125127
{
126128
/// Returns closest entity whose shape, as indexed by systems registered by
127-
/// [`super::systems::IndexPlugin`], intersects a given ray.
129+
/// [`super::PreciseIndexPlugin`], intersects a given ray.
128130
///
129131
/// # Arguments
130132
///
@@ -173,7 +175,7 @@ where
173175
}
174176

175177
/// Returns true if queried solid object on the map, as indexed by
176-
/// [`super::systems::IndexPlugin`], intersects with the given collider.
178+
/// [`super::PreciseIndexPlugin`], intersects with the given collider.
177179
pub fn collides(&self, collider: &impl ColliderWithCache) -> bool {
178180
let candidate_sets = self.index.query_aabb(collider.world_aabb());
179181
candidate_sets.flatten().any(|candidate| {

crates/index/src/systems.rs renamed to crates/index/src/precise/mod.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
//! Module with systems and a Bevy plugin for automatic entity indexing of
2-
//! solid entities.
3-
1+
//! This module implements 2D object partitioning for fast but precise object
2+
//! collider geometry lookup, for example ray casting.
3+
//!
4+
//! The core structure is a square tile grid which points to Bevy ECS entities.
5+
//! Newly spawned entities are automatically added, despawned entities removed
6+
//! and moved entities updated by systems added by [`PreciseIndexPlugin`].
47
use bevy::prelude::*;
58
use de_core::{
69
gamestate::GameState,
@@ -11,8 +14,17 @@ use de_core::{
1114
use de_objects::SolidObjects;
1215
use parry3d::math::Isometry;
1316

14-
use super::index::EntityIndex;
15-
use crate::collider::LocalCollider;
17+
pub use self::{
18+
collider::{ColliderWithCache, LocalCollider, QueryCollider},
19+
index::{EntityIndex, RayEntityIntersection, SpatialQuery},
20+
};
21+
22+
mod aabb;
23+
mod collider;
24+
mod grid;
25+
mod index;
26+
mod range;
27+
mod segment;
1628

1729
type SolidEntityQuery<'w, 's> = Query<
1830
'w,
@@ -38,29 +50,29 @@ type MovedQuery<'w, 's> =
3850
/// insert newly spawned solid entities to the index, update their position
3951
/// when [`bevy::prelude::Transform`] is changed and remove the entities from
4052
/// the index when they are de-spawned.
41-
pub(crate) struct IndexPlugin;
53+
pub(super) struct PreciseIndexPlugin;
4254

43-
impl Plugin for IndexPlugin {
55+
impl Plugin for PreciseIndexPlugin {
4456
fn build(&self, app: &mut App) {
4557
app.add_systems(OnEnter(AppState::InGame), setup)
4658
.add_systems(OnExit(AppState::InGame), cleanup)
4759
.add_systems(
4860
PostUpdate,
4961
(insert, remove)
5062
.run_if(in_state(GameState::Playing))
51-
.in_set(IndexSet::Index),
63+
.in_set(PreciseIndexSet::Index),
5264
)
5365
.add_systems(
5466
PostMovement,
5567
update
5668
.run_if(in_state(GameState::Playing))
57-
.in_set(IndexSet::Index),
69+
.in_set(PreciseIndexSet::Index),
5870
);
5971
}
6072
}
6173

6274
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, SystemSet)]
63-
pub enum IndexSet {
75+
pub enum PreciseIndexSet {
6476
Index,
6577
}
6678

crates/index/src/range.rs renamed to crates/index/src/precise/range.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::TILE_SIZE;
88
///
99
/// The tiles are iterated row-by-row, for example: (1, 1) -> (2, 1) -> (1, 2)
1010
/// -> (2, 2).
11-
pub(crate) struct TileRange {
11+
pub(super) struct TileRange {
1212
a: IVec2,
1313
b: IVec2,
1414
x: i32,
@@ -21,7 +21,7 @@ impl TileRange {
2121
///
2222
/// Tiles are assumed to be topologically closed. In other words, both
2323
/// touching and intersecting tiles are included in the range.
24-
pub(crate) fn from_aabb(aabb: &Aabb) -> Self {
24+
pub(super) fn from_aabb(aabb: &Aabb) -> Self {
2525
let aabb = aabb.to_flat();
2626
let min_flat: Vec2 = aabb.mins.into();
2727
let max_flat: Vec2 = aabb.maxs.into();
@@ -35,7 +35,7 @@ impl TileRange {
3535
/// * `a` - inclusive range start.
3636
///
3737
/// * `b` - inclusive range end.
38-
pub(crate) fn new(a: IVec2, b: IVec2) -> Self {
38+
pub(super) fn new(a: IVec2, b: IVec2) -> Self {
3939
Self {
4040
a,
4141
b,
@@ -46,12 +46,12 @@ impl TileRange {
4646
}
4747

4848
/// Returns true if the given point is not contained in the tile range.
49-
pub(crate) fn excludes(&self, point: IVec2) -> bool {
49+
pub(super) fn excludes(&self, point: IVec2) -> bool {
5050
self.a.cmpgt(point).any() || self.b.cmplt(point).any()
5151
}
5252

5353
/// Returns intersecting tile range. The result might be empty.
54-
pub(crate) fn intersection(&self, other: &TileRange) -> TileRange {
54+
pub(super) fn intersection(&self, other: &TileRange) -> TileRange {
5555
Self::new(self.a.max(other.a), self.b.min(other.b))
5656
}
5757
}

crates/index/src/segment.rs renamed to crates/index/src/precise/segment.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use de_types::projection::ToFlat;
66
use glam::{IVec2, Vec2};
77
use parry3d::shape::Segment;
88

9-
use super::{grid::TileGrid, TILE_SIZE};
9+
use super::grid::TileGrid;
10+
use crate::TILE_SIZE;
1011

1112
/// An iterator over sets of entities from tiles intersecting a given line
1213
/// segment.
@@ -18,14 +19,14 @@ use super::{grid::TileGrid, TILE_SIZE};
1819
/// The tiles (and thus the yielded sets) are iterated by increasing distance
1920
/// between point `a` of the given line segment and the intersection of the
2021
/// tile with the line segment.
21-
pub(crate) struct SegmentCandidates<'a> {
22+
pub(super) struct SegmentCandidates<'a> {
2223
grid: &'a TileGrid,
2324
tiles: TileIterator,
2425
encountered: Option<&'a AHashSet<Entity>>,
2526
}
2627

2728
impl<'a> SegmentCandidates<'a> {
28-
pub(crate) fn new(grid: &'a TileGrid, segment: Segment) -> Self {
29+
pub(super) fn new(grid: &'a TileGrid, segment: Segment) -> Self {
2930
Self {
3031
grid,
3132
tiles: TileIterator::new(segment),
@@ -172,7 +173,6 @@ mod tests {
172173
use parry3d::{bounding_volume::Aabb, math::Point, shape::Segment};
173174

174175
use super::*;
175-
use crate::grid::TileGrid;
176176

177177
#[test]
178178
fn test_segment_candidates() {

crates/spawner/src/draft.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use de_core::{
1212
objects::{MovableSolid, ObjectTypeComponent, StaticSolid},
1313
state::AppState,
1414
};
15-
use de_index::{ColliderWithCache, IndexSet, QueryCollider, SpatialQuery};
15+
use de_index::{ColliderWithCache, PreciseIndexSet, QueryCollider, SpatialQuery};
1616
use de_map::size::MapBounds;
1717
use de_objects::{AssetCollection, SceneType, Scenes, SolidObjects, EXCLUSION_OFFSET};
1818
use de_types::{
@@ -42,7 +42,7 @@ impl Plugin for DraftPlugin {
4242
PostUpdate,
4343
(update_draft, check_draft_loaded, update_draft_colour)
4444
.run_if(in_state(GameState::Playing))
45-
.after(IndexSet::Index),
45+
.after(PreciseIndexSet::Index),
4646
);
4747
}
4848
}

0 commit comments

Comments
 (0)