Compare commits

..

No commits in common. "b4ad5000013409cfc892691925c555a6a25d61c8" and "3d7663e0324d34df51ce43a325a9449015372ef8" have entirely different histories.

3 changed files with 117 additions and 142 deletions

View file

@ -8,7 +8,7 @@ use rstar::{primitives::GeomWithData, RTree};
mod systems; mod systems;
pub use systems::{ pub use systems::{
handle_inputs, init_naybs, setup_channels, spawn_background, turkey_time, update_buddies, init_naybs, rotate_camera, setup_channels, spawn_background, turkey_time, update_buddies,
update_gizmos, update_naybs, update_pos, update_vel, update_gizmos, update_naybs, update_pos, update_vel,
}; };
@ -59,8 +59,8 @@ struct IndexMessage {
struct RawMessage(Vec<EntityPos>); struct RawMessage(Vec<EntityPos>);
#[derive(Resource, Deref, DerefMut)] #[derive(Default, Resource, Deref, DerefMut)]
pub struct BackgroundHandle(bevy::tasks::Task<()>); pub struct BackgroundHandle(Option<bevy::tasks::Task<()>>);
#[derive(Component)] #[derive(Component)]
pub struct Toid { pub struct Toid {
@ -70,6 +70,3 @@ pub struct Toid {
#[derive(Debug, Default, Clone, Copy, Deref, DerefMut, Resource)] #[derive(Debug, Default, Clone, Copy, Deref, DerefMut, Resource)]
pub struct LookAt(Vec3); pub struct LookAt(Vec3);
#[derive(Resource, Debug, Deref, DerefMut, Clone, Default)]
pub struct Paused(bool);

View file

@ -2,18 +2,16 @@ use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
input::keyboard::Key, input::keyboard::Key,
prelude::{ prelude::{
children, default, px, warn, App, AssetServer, Assets, ButtonInput, Camera3d, ClearColor, children, default, px, App, AssetServer, Assets, ButtonInput, Camera3d, ClearColor, Color,
Color, Commands, DefaultPlugins, Entity, FontSize, GlobalAmbientLight, IntoScheduleConfigs, Commands, DefaultPlugins, Entity, FontSize, GlobalAmbientLight, IntoScheduleConfigs, Mesh,
Mesh, Mesh3d, MeshMaterial3d, Node, Plane3d, PositionType, Query, Res, ResMut, Mesh3d, MeshMaterial3d, Node, Plane3d, PositionType, Query, Res, ResMut, StandardMaterial,
StandardMaterial, Startup, Text, TextFont, Transform, Update, Vec2, Vec3, Visibility, Startup, Text, TextFont, Transform, Update, Vec2, Vec3, Visibility, Window,
Window, With,
}, },
}; };
use rstar::RTree; use rstar::RTree;
use toids::{ use toids::{
handle_inputs, init_naybs, setup_channels, spawn_background, turkey_time, update_buddies, init_naybs, rotate_camera, setup_channels, spawn_background, turkey_time, update_buddies,
update_naybs, update_pos, update_vel, BackgroundHandle, Config, LookAt, Naybs, Paused, update_naybs, update_pos, update_vel, BackgroundHandle, Config, LookAt, Naybs, Positions,
Positions,
}; };
fn main() { fn main() {
@ -25,7 +23,6 @@ fn main() {
LogDiagnosticsPlugin::default(), LogDiagnosticsPlugin::default(),
)) ))
.insert_resource(config) .insert_resource(config)
.insert_resource(Paused::default())
.insert_resource(Positions::default()) .insert_resource(Positions::default())
.insert_resource(ClearColor(Color::srgb(0.64, 0.745, 0.937))) // a nice light blue .insert_resource(ClearColor(Color::srgb(0.64, 0.745, 0.937))) // a nice light blue
.insert_resource(GlobalAmbientLight { .insert_resource(GlobalAmbientLight {
@ -38,20 +35,16 @@ fn main() {
index: RTree::new(), index: RTree::new(),
positions: None, positions: None,
}) })
.insert_resource(BackgroundHandle::default())
.insert_resource(LookAt::default()) .insert_resource(LookAt::default())
.add_systems( .add_systems(
Startup, Startup,
(setup_channels, setup, init_naybs, spawn_background).chain(), (setup_channels, setup, init_naybs, spawn_background).chain(),
) )
.add_systems(Update, (update_pos, update_buddies, update_vel).chain())
.add_systems( .add_systems(
Update, Update,
(update_pos, update_buddies, update_vel) (rotate_camera, close_on_esc, update_naybs.after(update_pos)),
.chain()
.run_if(should_run),
)
.add_systems(
Update,
(handle_inputs, close_on_esc, update_naybs.after(update_pos)),
) )
//.add_systems(Update, update_gizmos) //.add_systems(Update, update_gizmos)
.run(); .run();
@ -79,8 +72,10 @@ fn setup(
Transform::default(), Transform::default(),
)); ));
// let toid_model = models.load("models/boid.glb#Scene0");
for _ in 0..config.toids { for _ in 0..config.toids {
turkey_time(&mut commands, rand, &models, &mut positions); turkey_time(&mut commands, rand, &models, &mut positions.0);
} }
// instructions // instructions
@ -93,7 +88,7 @@ fn setup(
..default() ..default()
}, },
children![( children![(
Text::new("Up and down for camera forward and back; hold shift to change height.\nLeft or right to move left or right.\nPress 'SPACE' to pause, 'ESC' to quit."), Text::new("Up and down for camera forward and back; hold shift to change height.\nLeft or right to move left or right.\nPress 'ESC' to quit."),
TextFont { TextFont {
font_size: FontSize::Px(20.0), font_size: FontSize::Px(20.0),
..default() ..default()
@ -104,27 +99,18 @@ fn setup(
fn close_on_esc( fn close_on_esc(
mut commands: Commands, mut commands: Commands,
windows: Query<Entity, With<Window>>, focused_windows: Query<(Entity, &Window)>,
input: Res<ButtonInput<Key>>, input: Res<ButtonInput<Key>>,
handle: ResMut<BackgroundHandle>, mut handle: ResMut<BackgroundHandle>,
) { ) {
let background_gone = handle.as_ref().is_finished(); for (window, focus) in focused_windows.iter() {
if background_gone { if !focus.focused {
warn!("background is gone, shutting down"); continue;
} }
let exited = background_gone || input.just_pressed(Key::Escape); if input.just_pressed(Key::Escape) {
if !exited { drop((*handle).take());
return; commands.entity(window).despawn();
} }
drop(handle);
for window in windows.iter() {
commands.entity(window).despawn();
} }
} }
fn should_run(paused: Res<Paused>) -> bool {
!**paused
}

View file

@ -5,21 +5,23 @@ use bevy::{input::keyboard::Key, prelude::*, tasks::AsyncComputeTaskPool};
use rand::RngExt; use rand::RngExt;
use super::{ use super::{
BackgroundHandle, Buddies, GeomWithData, IndexChannel, IndexMessage, LookAt, Naybs, Paused, BackgroundHandle, Buddies, GeomWithData, IndexChannel, IndexMessage, LookAt, Naybs, Positions,
Positions, RTree, RawMessage, RawPosChannel, Toid, Velocity, RTree, RawMessage, RawPosChannel, Toid, Velocity,
}; };
// toid stuff // toid stuff
const SPEED: f32 = 12.0; const SPEED: f32 = 6.0;
const SPEED_DIFF_RANGE: f32 = 0.1; const SPEED_DIFF_RANGE: f32 = 0.2; // +/- 10%
const MAX_DELTA_V: f32 = std::f32::consts::PI * 1.2; const MAX_DELTA_V: f32 = std::f32::consts::PI * 1.5; // basically 360
// degrees/sec
// how far from origin before really wanting to come back // how far from origin before really wanting to come back
const RADIUS: f32 = 30.0; const RADIUS: f32 = 40.0;
// how close to try to stay to your buddies // how close to try to stay to your buddies
const BUDDY_RADIUS: f32 = SPEED; const BUDDY_RADIUS: f32 = 10.0;
const MIN_ALTITUDE: f32 = 13.5; const MIN_ALTITUDE: f32 = 3.5;
pub fn setup_channels(mut commands: Commands) { pub fn setup_channels(mut commands: Commands) {
let (raw_tx, raw_rx) = bounded(1); let (raw_tx, raw_rx) = bounded(1);
@ -44,6 +46,7 @@ pub fn update_naybs(
if let Ok(idx_msg) = idx_chan.rx.try_recv() { if let Ok(idx_msg) = idx_chan.rx.try_recv() {
naybs.index = idx_msg.index; naybs.index = idx_msg.index;
let _ = naybs.positions.insert(idx_msg.raw); let _ = naybs.positions.insert(idx_msg.raw);
return;
} }
if let Some(mut raw) = naybs.positions.take() { if let Some(mut raw) = naybs.positions.take() {
@ -67,9 +70,9 @@ pub fn init_naybs(positions: Res<Positions>, mut naybs: ResMut<Naybs>) {
} }
pub fn spawn_background( pub fn spawn_background(
mut commands: Commands,
mut raw_chan: ResMut<RawPosChannel>, mut raw_chan: ResMut<RawPosChannel>,
idx_chan: Res<IndexChannel>, idx_chan: Res<IndexChannel>,
mut handle: ResMut<BackgroundHandle>,
) { ) {
let pool = AsyncComputeTaskPool::get(); let pool = AsyncComputeTaskPool::get();
@ -79,7 +82,6 @@ pub fn spawn_background(
let task = pool.spawn(async move { let task = pool.spawn(async move {
let tx = idx_tx; let tx = idx_tx;
let rx = raw_rx; let rx = raw_rx;
loop { loop {
match rx.recv().await { match rx.recv().await {
Ok(RawMessage(e_posns)) => { Ok(RawMessage(e_posns)) => {
@ -102,7 +104,7 @@ pub fn spawn_background(
} }
}); });
commands.insert_resource(BackgroundHandle(task)); let _ = handle.0.insert(task);
} }
pub fn turkey_time( pub fn turkey_time(
@ -151,71 +153,69 @@ pub fn update_vel(
) { ) {
let dt = time.delta_secs(); let dt = time.delta_secs();
let max_delta = MAX_DELTA_V * dt; let max_delta = MAX_DELTA_V * dt;
toids for (xform, mut vel, toid, buddies, entity) in toids.iter_mut() {
.par_iter_mut() let speed = toid.speed;
.for_each(|(xform, mut vel, toid, buddies, entity)| { let mut dir = vel.normalize();
let speed = toid.speed; let pos = xform.translation;
let mut dir = vel.normalize(); let original_dir = dir;
let pos = xform.translation;
let original_dir = dir;
// find buddies and orient; point more towards further-away buddies // find buddies and orient; point more towards further-away buddies
for buddy in buddies.iter() { for buddy in buddies.0.iter() {
let bp = positions.get(buddy).unwrap(); let bp = positions.get(buddy).unwrap();
let bdir = *bp - pos; let bdir = *bp - pos;
let dist = bdir.length(); let dist = bdir.length();
let rot = Quat::from_rotation_arc(dir, bdir.normalize()); let rot = Quat::from_rotation_arc(dir, bdir.normalize());
let s = (dist / (BUDDY_RADIUS * 1.2)).min(1.0); let s = (dist / (BUDDY_RADIUS * 1.2)).min(1.0);
let rot = Quat::IDENTITY.slerp(rot, s); let rot = Quat::IDENTITY.slerp(rot, s);
dir = rot.mul_vec3(dir).normalize(); dir = rot.mul_vec3(dir).normalize();
} }
// avoid flying into neighbors // avoid flying into neighbors
let min_dist = speed; let min_dist = speed;
for neighbor in index for neighbor in index
.index .index
.locate_within_distance(pos.to_array(), speed.powi(2)) .locate_within_distance(pos.to_array(), min_dist)
.filter(|n| n.data != entity) .filter(|n| n.data != entity)
{ {
let bp = Vec3::from_array(*neighbor.geom()); let bp = Vec3::from_array(*neighbor.geom());
let bdir = pos - bp; let bdir = pos - bp;
let dist = bdir.length(); let dist = bdir.length();
let s = 1.0 - (dist / min_dist).min(1.0); let s = 1.0 - (dist / min_dist).min(1.0);
let rot = Quat::from_rotation_arc(dir, bdir.normalize()); let rot = Quat::from_rotation_arc(dir, bdir.normalize());
let rot = Quat::IDENTITY.slerp(rot, s); let rot = Quat::IDENTITY.slerp(rot, s);
dir = rot.mul_vec3(dir).normalize(); dir = rot.mul_vec3(dir).normalize();
} }
// nudge toward origin if too far // nudge toward origin if too far
{ {
let dist = pos.length(); let dist = pos.length();
let toward_origin = -pos.normalize(); let toward_origin = -pos.normalize();
let s = (dist / RADIUS).min(1.0); let s = (dist / RADIUS).min(1.0);
let rot = Quat::from_rotation_arc(dir, toward_origin); let rot = Quat::from_rotation_arc(dir, toward_origin);
let rot = Quat::IDENTITY.slerp(rot, s); let rot = Quat::IDENTITY.slerp(rot, s);
dir = rot.mul_vec3(dir).normalize(); dir = rot.mul_vec3(dir).normalize();
} }
// nudge up if too low // nudge up if too low
if pos.y < MIN_ALTITUDE { if pos.y < MIN_ALTITUDE {
let dh = MIN_ALTITUDE - pos.y; let dh = MIN_ALTITUDE - pos.y;
let s = (dh / MIN_ALTITUDE).min(1.0); let s = (dh / MIN_ALTITUDE).min(1.0);
let rot = Quat::from_rotation_arc(dir, Vec3::Y); let rot = Quat::from_rotation_arc(dir, Vec3::Y);
let rot = Quat::IDENTITY.slerp(rot, s); let rot = Quat::IDENTITY.slerp(rot, s);
dir = rot.mul_vec3(dir).normalize(); dir = rot.mul_vec3(dir).normalize();
} }
// make sure velocity doesn't change too suddenly // make sure velocity doesn't change too suddenly
let delta = dir.dot(original_dir).acos(); let delta = dir.dot(original_dir).acos();
if delta > max_delta { if delta > max_delta {
let s = max_delta / delta; let s = max_delta / delta;
let rot = Quat::from_rotation_arc(original_dir, dir); let rot = Quat::from_rotation_arc(original_dir, dir);
let rot = Quat::IDENTITY.slerp(rot, s); let rot = Quat::IDENTITY.slerp(rot, s);
dir = rot.mul_vec3(original_dir).normalize(); dir = rot.mul_vec3(original_dir).normalize();
} }
**vel = dir * speed; **vel = dir * speed;
}); }
} }
pub fn update_pos( pub fn update_pos(
@ -230,7 +230,7 @@ pub fn update_pos(
let look_at = xform.translation + vel.0; let look_at = xform.translation + vel.0;
xform.translation += vel.0 * dt; xform.translation += vel.0 * dt;
xform.look_at(look_at, Vec3::Y); xform.look_at(look_at, Vec3::Y);
positions.insert(entity, xform.translation); *positions.entry(entity).or_insert(Vec3::ZERO) = xform.translation;
new_look += xform.translation; new_look += xform.translation;
} }
**lookat = new_look / positions.len() as f32; **lookat = new_look / positions.len() as f32;
@ -242,30 +242,28 @@ pub fn update_buddies(
positions: Res<Positions>, positions: Res<Positions>,
) { ) {
let d2 = (BUDDY_RADIUS * 1.5).powi(2); let d2 = (BUDDY_RADIUS * 1.5).powi(2);
toids for (xform, entity, toid, mut buddies) in toids.iter_mut() {
.par_iter_mut() let pos = xform.translation;
.for_each(|(xform, entity, toid, mut buddies)| { for buddy in buddies.clone().iter() {
let pos = xform.translation; let bp = positions[buddy];
for buddy in buddies.clone().iter() { let bd2 = (bp - pos).length_squared();
let bp = positions.get(buddy).unwrap(); if bd2 > d2 {
let bd2 = (bp - pos).length_squared(); buddies.remove(buddy);
if bd2 > d2 {
buddies.remove(buddy);
}
} }
}
if buddies.len() < toid.buddies { if buddies.len() < toid.buddies {
let diff = toid.buddies - buddies.len(); let diff = toid.buddies - buddies.len();
for neighbor in index for neighbor in index
.index .index
.nearest_neighbor_iter(pos.to_array()) .nearest_neighbor_iter(pos.to_array())
.filter(|n| n.data != entity) .filter(|n| n.data != entity)
.take(diff) .take(diff)
{ {
buddies.insert(neighbor.data); buddies.insert(neighbor.data);
}
} }
}); }
}
} }
pub fn update_gizmos(toids: Query<&Transform, With<Toid>>, mut gizmos: Gizmos) { pub fn update_gizmos(toids: Query<&Transform, With<Toid>>, mut gizmos: Gizmos) {
@ -280,17 +278,11 @@ pub fn update_gizmos(toids: Query<&Transform, With<Toid>>, mut gizmos: Gizmos) {
} }
} }
pub fn handle_inputs( pub fn rotate_camera(
mut query: Query<&mut Transform, With<Camera>>, mut query: Query<&mut Transform, With<Camera>>,
mut paused: ResMut<Paused>,
keys: Res<ButtonInput<Key>>, keys: Res<ButtonInput<Key>>,
lookat: Res<LookAt>, lookat: Res<LookAt>,
) { ) {
if keys.just_pressed(Key::Space) {
let pause = **paused;
**paused = !pause;
}
let mut xform = query.single_mut().unwrap(); let mut xform = query.single_mut().unwrap();
let forward = xform.forward().as_vec3() * 0.7; let forward = xform.forward().as_vec3() * 0.7;