From 4a9c2bdb0291bb606630b0cbc3eef8ec095342c1 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sun, 20 Sep 2026 17:06:11 -0700 Subject: [PATCH] runs about 4x as fast as previous --- Cargo.lock | 10 +++- Cargo.toml | 7 +-- src/lib.rs | 129 ++++++++++++++++++++++++++++++++++++++++++++++------ src/main.rs | 33 ++++++++++---- 4 files changed, 154 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1387010..c9503ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -865,6 +865,7 @@ dependencies = [ "bevy_reflect", "bevy_render", "bevy_shader", + "bevy_sprite_render", "bevy_transform", "bevy_utils", "bytemuck", @@ -963,6 +964,7 @@ dependencies = [ "bevy_ecs", "bevy_input", "bevy_math", + "bevy_picking", "bevy_reflect", "bevy_window", "log", @@ -1003,6 +1005,7 @@ dependencies = [ "bevy_math", "bevy_mesh", "bevy_pbr", + "bevy_picking", "bevy_platform", "bevy_post_process", "bevy_ptr", @@ -1018,6 +1021,8 @@ dependencies = [ "bevy_time", "bevy_transform", "bevy_ui", + "bevy_ui_render", + "bevy_ui_widgets", "bevy_utils", "bevy_window", "bevy_winit", @@ -1224,11 +1229,13 @@ dependencies = [ "bevy_ecs", "bevy_input", "bevy_math", + "bevy_mesh", "bevy_platform", "bevy_reflect", "bevy_time", "bevy_transform", "bevy_window", + "crossbeam-channel", "tracing", "uuid", ] @@ -1464,6 +1471,7 @@ dependencies = [ "bevy_log", "bevy_math", "bevy_mesh", + "bevy_picking", "bevy_reflect", "bevy_text", "bevy_transform", @@ -4787,8 +4795,8 @@ name = "toids" version = "0.1.0" dependencies = [ "argh", + "async-channel", "bevy", - "crossbeam-channel", "rand", "rstar", ] diff --git a/Cargo.toml b/Cargo.toml index 0001e9b..18f7dfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,12 @@ name = "toids" version = "0.1.0" edition = "2024" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +debug = ["bevy/debug"] [dependencies] argh = "0.1.12" -bevy = { version = "0.19", default-features = false, features = ["default_app", "default_platform", "bevy_scene", "3d_bevy_render", "bevy_ui"] } -crossbeam-channel = "0.5" +bevy = { version = "0.19", default-features = false, features = ["3d", "ui"] } +async-channel = "2" rand = { version = "0.10", features = ["thread_rng"] } rstar = "0.13" diff --git a/src/lib.rs b/src/lib.rs index 7547a08..7118f4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,15 @@ use std::collections::{HashMap, HashSet}; use argh::FromArgs; +use async_channel::{bounded, Receiver, Sender}; use bevy::{input::keyboard::Key, prelude::*, tasks::AsyncComputeTaskPool}; -use crossbeam_channel::{bounded, Receiver, Sender}; use rand::RngExt; use rstar::{primitives::GeomWithData, RTree}; -type Index = RTree>; +type IndexElement = GeomWithData<[f32; 3], Entity>; +type Index = RTree; + +type EntityPos = (Vec3, Entity); // toid stuff const SPEED: f32 = 2.0; @@ -41,22 +44,32 @@ pub struct Velocity(Vec3); #[derive(Debug, Default, Resource)] pub struct Naybs { - index: Index, - positions: Option>, + pub index: Index, + pub positions: Option>, } #[derive(Resource)] pub struct RawPosChannel { - tx: Sender>, - rx: Receiver>, + tx: Sender, + rx: Option>, } #[derive(Resource)] pub struct IndexChannel { - tx: Sender<(Box<[Vec3]>, Index)>, - rx: Receiver<(Box<[Vec3]>, Index)>, + tx: Sender, + rx: Receiver, } +struct IndexMessage { + raw: Vec, + index: Index, +} + +struct RawMessage(Vec); + +#[derive(Default, Resource, Deref, DerefMut)] +pub struct BackgroundHandle(Option>); + #[derive(Component)] pub struct Toid { pub speed: f32, @@ -66,11 +79,96 @@ pub struct Toid { #[derive(Debug, Default, Clone, Copy, Deref, DerefMut, Resource)] pub struct LookAt(Vec3); +pub fn setup_channels(mut commands: Commands) { + let (raw_tx, raw_rx) = bounded(1); + commands.insert_resource(RawPosChannel { + tx: raw_tx, + rx: Some(raw_rx), + }); + + let (idx_tx, idx_rx) = bounded(1); + commands.insert_resource(IndexChannel { + tx: idx_tx, + rx: idx_rx, + }); +} + +pub fn update_naybs( + mut naybs: ResMut, + raw_chan: Res, + positions: Res, + idx_chan: Res, +) { + if let Ok(idx_msg) = idx_chan.rx.try_recv() { + naybs.index = idx_msg.index; + let _ = naybs.positions.insert(idx_msg.raw); + return; + } + + if let Some(mut raw) = naybs.positions.take() { + for (i, (entity, pos)) in positions.iter().enumerate() { + raw[i] = (*pos, *entity); + } + + if let Err(e) = raw_chan.tx.try_send(RawMessage(raw)) { + let RawMessage(raw) = e.into_inner(); + let _ = naybs.positions.insert(raw); + } + } +} + +pub fn init_naybs(positions: Res, mut naybs: ResMut) { + let mut raw = Vec::with_capacity(positions.len()); + for (entity, pos) in positions.iter() { + raw.push((*pos, *entity)); + } + naybs.positions = Some(raw); +} + +pub fn spawn_background( + mut raw_chan: ResMut, + idx_chan: Res, + mut handle: ResMut, +) { + let pool = AsyncComputeTaskPool::get(); + + let raw_rx = raw_chan.rx.take().unwrap(); + let idx_tx = idx_chan.tx.clone(); + + let task = pool.spawn(async move { + let tx = idx_tx; + let rx = raw_rx; + loop { + match rx.recv().await { + Ok(RawMessage(e_posns)) => { + let idx = RTree::bulk_load( + e_posns + .iter() + .map(|(p, e)| GeomWithData::new(p.to_array(), *e)) + .collect(), + ); + let msg = IndexMessage { + raw: e_posns, + index: idx, + }; + if tx.send(msg).await.is_err() { + break; + }; + } + Err(_) => break, + } + } + }); + + let _ = handle.0.insert(task); +} + pub fn turkey_time( commands: &mut Commands, r: &mut impl rand::prelude::Rng, assets: &AssetServer, -) -> Entity { + positions: &mut HashMap, +) { let speed_diff = r.random_range(-SPEED_DIFF_RANGE..=SPEED_DIFF_RANGE); let speed = SPEED + (SPEED * speed_diff); let vel = unit_vec(r) * speed; @@ -78,24 +176,29 @@ pub fn turkey_time( let x = r.random_range(-10.0..=10.0); let z = r.random_range(-10.0..=10.0); let y = r.random_range(MIN_ALTITUDE..=(MIN_ALTITUDE + 5.5)); - commands + + let entity = commands .spawn(( Velocity(vel), Buddies::default(), Toid { speed, buddies }, Transform::from_xyz(x, y, z), + Visibility::Visible, )) .with_children(|t| { t.spawn(WorldAssetRoot( - assets.load(GltfAssetLabel::Scene(0).from_asset("models/toid.glb")), + assets.load(GltfAssetLabel::Scene(0).from_asset("models/boid.glb")), )) - .insert(Transform::default()); + .insert(Transform::default()) + .insert(Visibility::Visible); // .insert(Transform::from_rotation(Quat::from_axis_angle( // Vec3::Y, // -std::f32::consts::FRAC_PI_2, // ))); }) - .id() + .id(); + + positions.insert(entity, Vec3::new(x, y, z)); } pub fn update_vel( diff --git a/src/main.rs b/src/main.rs index faabbfa..7933a5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ -use std::time::Duration; - use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, input::keyboard::Key, prelude::*, }; +use rstar::RTree; use toids::{ - rotate_camera, turkey_time, update_buddies, update_pos, update_vel, Config, LookAt, Positions, + init_naybs, rotate_camera, setup_channels, spawn_background, turkey_time, update_buddies, + update_naybs, update_pos, update_vel, BackgroundHandle, Config, LookAt, Naybs, Positions, }; fn main() { @@ -22,13 +22,25 @@ fn main() { .insert_resource(ClearColor(Color::srgb(0.64, 0.745, 0.937))) // a nice light blue .insert_resource(GlobalAmbientLight { color: Color::WHITE, - brightness: 1.0, + brightness: 150.0, + affects_lightmapped_meshes: true, ..Default::default() }) + .insert_resource(Naybs { + index: RTree::new(), + positions: None, + }) + .insert_resource(BackgroundHandle::default()) .insert_resource(LookAt::default()) - .add_systems(Startup, setup) + .add_systems( + Startup, + (setup_channels, setup, init_naybs, spawn_background).chain(), + ) .add_systems(Update, (update_pos, update_buddies, update_vel).chain()) - .add_systems(Update, (rotate_camera, close_on_esc)) + .add_systems( + Update, + (rotate_camera, close_on_esc, update_naybs.after(update_pos)), + ) //.add_systems(Update, update_gizmos) .run(); } @@ -37,6 +49,7 @@ fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, + mut positions: ResMut, config: Res, models: Res, ) { @@ -49,18 +62,20 @@ fn setup( // plane commands.spawn(( Mesh3d(meshes.add(Mesh::from(Plane3d::new(Vec3::Y, Vec2::splat(500.0))))), - MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), + MeshMaterial3d(materials.add(Color::srgb(0.3, 0.9, 0.3))), + Visibility::Visible, Transform::default(), )); // let toid_model = models.load("models/boid.glb#Scene0"); for _ in 0..config.toids { - let _ = turkey_time(&mut commands, rand, &models); + turkey_time(&mut commands, rand, &models, &mut positions.0); } // instructions commands.spawn(( + Visibility::Visible, Node { position_type: PositionType::Absolute, top: px(12.0), @@ -81,6 +96,7 @@ fn close_on_esc( mut commands: Commands, focused_windows: Query<(Entity, &Window)>, input: Res>, + mut handle: ResMut, ) { for (window, focus) in focused_windows.iter() { if !focus.focused { @@ -88,6 +104,7 @@ fn close_on_esc( } if input.just_pressed(Key::Escape) { + drop((*handle).take()); commands.entity(window).despawn(); } }