diff --git a/Cargo.toml b/Cargo.toml index 48ee500..52731ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ readme = "README.md" authors = ["laund "] [dependencies] -bevy = { version = "0.16", default-features = false } +bevy = { version = "0.19", default-features = false } # KD-Tree dependencies kd-tree = { version = "0.6.0", optional = true } typenum = { version = "1.18.0" } @@ -23,7 +23,7 @@ kdtree_rayon = ["kdtree", "kd-tree/rayon"] kdtree = ["dep:kd-tree"] [dev-dependencies] -bevy = { version = "0.16" } +bevy = { version = "0.19" } rand = "0.8" [profile.dev] diff --git a/examples/distance3d.rs b/examples/distance3d.rs index 9affa47..88a74b6 100644 --- a/examples/distance3d.rs +++ b/examples/distance3d.rs @@ -41,7 +41,6 @@ fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, - mut ambient_light: ResMut, ) { let handles = MaterialHandles { orange_red: materials.add(Color::from(csscolors::ORANGE_RED)), @@ -50,8 +49,11 @@ fn setup( }; commands.insert_resource(handles.clone()); - ambient_light.color = Color::WHITE; - ambient_light.brightness = 500.; + commands.insert_resource(GlobalAmbientLight { + color: Color::WHITE, + brightness: 500., + ..default() + }); commands.spawn(( Camera3d::default(), diff --git a/examples/modify_timestep.rs b/examples/modify_timestep.rs index 37be19f..663a3eb 100644 --- a/examples/modify_timestep.rs +++ b/examples/modify_timestep.rs @@ -34,7 +34,7 @@ fn setup(mut commands: Commands) { commands.spawn(( Text("Click mouse to change rate".to_string()), TextFont { - font_size: 30.0, + font_size: FontSize::Px(30.0), ..default() }, TextColor(Color::BLACK), diff --git a/examples/within2d.rs b/examples/within2d.rs new file mode 100644 index 0000000..80ce8a2 --- /dev/null +++ b/examples/within2d.rs @@ -0,0 +1,171 @@ +use std::ops::Deref; +use std::time::Duration; + +use bevy::{ + color::palettes::css as csscolors, + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + math::Vec3Swizzles, + prelude::*, + window::PrimaryWindow, +}; +use bevy_spatial::{AutomaticUpdate, SpatialAABBAccess, SpatialStructure}; +use bevy_spatial::{SpatialAccess, kdtree::KDTree2}; +// marker for entities tracked by the KDTree +#[derive(Component, Default)] +struct NearestNeighbourComponent; + +// marker for the "cursor" entity +#[derive(Component)] +struct Cursor; + +fn main() { + App::new() + .add_plugins(DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + present_mode: bevy::window::PresentMode::AutoNoVsync, + ..default() + }), + ..default() + })) + // Add the plugin, which takes the tracked component as a generic. + .add_plugins( + AutomaticUpdate::::new() + .with_spatial_ds(SpatialStructure::KDTree2) + .with_frequency(Duration::from_millis(1)), + ) + .add_plugins(LogDiagnosticsPlugin::default()) + .add_plugins(FrameTimeDiagnosticsPlugin::default()) + .insert_resource(Mouse2D { pos: Vec2::ZERO }) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + update_mouse_pos, + ( + mouse, + color_rect, + reset_color.before(color_rect), + collide_wall, + movement, + ), + ) + .chain(), + ) + .run(); +} + +// type alias for easier usage later +type NNTree = KDTree2; + +fn setup(mut commands: Commands) { + commands.spawn(Camera2d); + commands.spawn(( + Cursor, + Sprite { + color: Color::srgb(0.0, 0.0, 1.0), + custom_size: Some(Vec2::new(10.0, 10.0)), + ..default() + }, + Transform { + translation: Vec3::ZERO, + ..default() + }, + )); + let sprite = Sprite { + color: csscolors::ORANGE_RED.into(), + custom_size: Some(Vec2::new(6.0, 6.0)), + ..default() + }; + for x in -100..100 { + for y in -100..100 { + commands.spawn(( + NearestNeighbourComponent, + sprite.clone(), + Transform { + translation: Vec3::new((x * 4) as f32, (y * 4) as f32, 0.0), + ..default() + }, + )); + } + } +} +#[derive(Copy, Clone, Resource)] +struct Mouse2D { + pos: Vec2, +} + +fn update_mouse_pos( + window: Single<&Window, With>, + camera: Single<(&Camera, &GlobalTransform)>, + mut mouse: ResMut, +) { + let (cam, cam_t) = camera.deref(); + if let Some(w_pos) = window.cursor_position() { + if let Ok(pos) = cam.viewport_to_world_2d(cam_t, w_pos) { + mouse.pos = pos; + } + } +} + +fn mouse( + mut commands: Commands, + mouse: Res, + treeaccess: Res, + ms_buttons: Res>, + mut query: Query<&mut Sprite, With>, +) { + let use_mouse = ms_buttons.pressed(MouseButton::Left); + + let p1 = mouse.pos; + let p2 = Vec2::from([100.0, -100.0]); + + for (_, entity) in treeaccess.within(p1, p2) { + if use_mouse { + commands.entity(entity.unwrap()).despawn(); + } + + } +} + +fn color_rect( + treeaccess: Res, + mouse: Res, + mut query: Query<&mut Sprite, With>, +) { + let p1 = mouse.pos; + let p2 = Vec2::from([100.0, -100.0]); + + for (_, entity) in treeaccess.within(p1, p2) { + if let Ok(mut sprite) = query.get_mut(entity.unwrap()) { + sprite.color = csscolors::GREEN.into(); + } + } +} + +fn reset_color(mut query: Query<&mut Sprite, With>) { + for mut sprite in &mut query { + sprite.color = csscolors::ORANGE_RED.into(); + } +} + +fn movement(mut query: Query<&mut Transform, With>) { + for mut pos in &mut query { + let goal = pos.translation - Vec3::ZERO; + pos.translation += goal.normalize_or_zero(); + } +} + +fn collide_wall( + window: Single<&Window, With>, + mut query: Query<&mut Transform, With>, +) { + let w = window.width() / 2.0; + let h = window.height() / 2.0; + + for mut pos in &mut query { + let [x, y] = pos.translation.xy().to_array(); + if y < -h || x < -w || y > h || x > w { + pos.translation = pos.translation.normalize_or_zero(); + } + } +} diff --git a/src/automatic_systems.rs b/src/automatic_systems.rs index af44b82..2426780 100644 --- a/src/automatic_systems.rs +++ b/src/automatic_systems.rs @@ -7,7 +7,10 @@ use crate::{ }; use bevy::{ - ecs::schedule::{ScheduleLabel, SystemSet}, + ecs::{ + component::Mutable, + schedule::{ScheduleLabel, SystemSet}, + }, prelude::*, }; @@ -29,7 +32,7 @@ pub(crate) struct AutoT(PhantomData); impl AutoT where GlamVec: VecFromTransform, - SpatialDS: UpdateSpatialAccess + Resource, + SpatialDS: UpdateSpatialAccess + Resource, ::Point: From<(Entity, GlamVec)>, SpatialDS::Comp: Component, { @@ -61,7 +64,7 @@ pub(crate) struct AutoGT(PhantomData); impl AutoGT where GlamVec: VecFromGlobalTransform, - SpatialDS: UpdateSpatialAccess + Resource, + SpatialDS: UpdateSpatialAccess + Resource, ::Point: From<(Entity, GlamVec)>, SpatialDS::Comp: Component, { diff --git a/src/kdtree.rs b/src/kdtree.rs index bfdadf9..00be227 100644 --- a/src/kdtree.rs +++ b/src/kdtree.rs @@ -6,7 +6,7 @@ use kd_tree::{KdPoint, KdTree as BaseKdTree, KdTreeN}; use crate::{ TComp, point::SpatialPoint, - spatial_access::{SpatialAccess, UpdateSpatialAccess}, + spatial_access::{SpatialAABBAccess, SpatialAccess, UpdateSpatialAccess}, }; use std::marker::PhantomData; @@ -47,6 +47,35 @@ macro_rules! kdtree_impl { } } + impl SpatialAABBAccess for $treename + where + Comp: TComp, + { + /// Return all points which are within the specified rectangular axis-aligned region. + fn within( + &self, + loc1: ::Vec, + loc2: ::Vec, + ) -> Vec { + let _span = info_span!("within").entered(); + + let p1: $pt = loc1.min(loc2).into(); + let p2: $pt = loc1.max(loc2).into(); + + let rect = [p1, p2]; + + if self.tree.len() == 0 { + vec![] + } else { + self.tree + .within(&rect) + .iter() + .map(|e| (e.vec(), e.entity())) + .collect() + } + } + } + impl SpatialAccess for $treename where Comp: TComp, diff --git a/src/lib.rs b/src/lib.rs index 76bf591..5346884 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ pub mod point; mod spatial_access; -pub use self::spatial_access::SpatialAccess; +pub use self::spatial_access::{SpatialAccess, SpatialAABBAccess}; use bevy::prelude::Component; mod timestep; diff --git a/src/spatial_access.rs b/src/spatial_access.rs index 435a8f1..2ca52e1 100644 --- a/src/spatial_access.rs +++ b/src/spatial_access.rs @@ -63,4 +63,11 @@ pub trait SpatialAccess: Send + Sync + 'static { ) -> Vec; } -// TODO: SpatialAABBAccess trait definition - should it be separate from SpatialAccess or depend on it? +pub trait SpatialAABBAccess: SpatialAccess { + /// Return all points which are within the specified rectangular axis-aligned region. + fn within( + &self, + loc1: ::Vec, + loc2: ::Vec, + ) -> Vec; +}