runs about 4x as fast as previous

This commit is contained in:
Joe Ardent 2026-09-20 17:06:11 -07:00
parent 279ebaa4c1
commit 4a9c2bdb02
4 changed files with 154 additions and 25 deletions

10
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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<GeomWithData<[f32; 3], Entity>>;
type IndexElement = GeomWithData<[f32; 3], Entity>;
type Index = RTree<IndexElement>;
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<Box<[Vec3]>>,
pub index: Index,
pub positions: Option<Vec<EntityPos>>,
}
#[derive(Resource)]
pub struct RawPosChannel {
tx: Sender<Box<[Vec3]>>,
rx: Receiver<Box<[Vec3]>>,
tx: Sender<RawMessage>,
rx: Option<Receiver<RawMessage>>,
}
#[derive(Resource)]
pub struct IndexChannel {
tx: Sender<(Box<[Vec3]>, Index)>,
rx: Receiver<(Box<[Vec3]>, Index)>,
tx: Sender<IndexMessage>,
rx: Receiver<IndexMessage>,
}
struct IndexMessage {
raw: Vec<EntityPos>,
index: Index,
}
struct RawMessage(Vec<EntityPos>);
#[derive(Default, Resource, Deref, DerefMut)]
pub struct BackgroundHandle(Option<bevy::tasks::Task<()>>);
#[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<Naybs>,
raw_chan: Res<RawPosChannel>,
positions: Res<Positions>,
idx_chan: Res<IndexChannel>,
) {
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<Positions>, mut naybs: ResMut<Naybs>) {
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<RawPosChannel>,
idx_chan: Res<IndexChannel>,
mut handle: ResMut<BackgroundHandle>,
) {
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<Entity, Vec3>,
) {
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(

View file

@ -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<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut positions: ResMut<Positions>,
config: Res<Config>,
models: Res<AssetServer>,
) {
@ -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<ButtonInput<Key>>,
mut handle: ResMut<BackgroundHandle>,
) {
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();
}
}