builds, doesn't run
This commit is contained in:
parent
7a97abdeb1
commit
279ebaa4c1
4 changed files with 3779 additions and 1661 deletions
5206
Cargo.lock
generated
5206
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
11
Cargo.toml
11
Cargo.toml
|
|
@ -1,12 +1,13 @@
|
|||
[package]
|
||||
name = "audubon"
|
||||
name = "toids"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.12"
|
||||
bevy = "0.12.0"
|
||||
bevy_spatial = "0.7.0"
|
||||
rand = "0.8.5"
|
||||
bevy = { version = "0.19", default-features = false, features = ["default_app", "default_platform", "bevy_scene", "3d_bevy_render", "bevy_ui"] }
|
||||
crossbeam-channel = "0.5"
|
||||
rand = { version = "0.10", features = ["thread_rng"] }
|
||||
rstar = "0.13"
|
||||
|
|
|
|||
137
src/lib.rs
137
src/lib.rs
|
|
@ -1,16 +1,18 @@
|
|||
use argh::FromArgs;
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
utils::{HashMap, HashSet},
|
||||
};
|
||||
use bevy_spatial::{kdtree::KDTree3, SpatialAccess};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub type NNTree = KDTree3<Toid>;
|
||||
use argh::FromArgs;
|
||||
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>>;
|
||||
|
||||
// toid stuff
|
||||
const SPEED: f32 = 2.0;
|
||||
const SPEED_DIFF_RANGE: f32 = 0.1; // +/- 10%
|
||||
const MAX_DELTA_V: f32 = std::f32::consts::PI * 2.0; // basically 360 degrees/sec
|
||||
const MAX_DELTA_V: f32 = std::f32::consts::PI * 2.0; // basically 360
|
||||
// degrees/sec
|
||||
|
||||
// how far from origin before really wanting to come back
|
||||
const RADIUS: f32 = 40.0;
|
||||
|
|
@ -37,6 +39,24 @@ pub struct Buddies(HashSet<Entity>);
|
|||
#[derive(Component, Debug, Clone, Deref, DerefMut, Default)]
|
||||
pub struct Velocity(Vec3);
|
||||
|
||||
#[derive(Debug, Default, Resource)]
|
||||
pub struct Naybs {
|
||||
index: Index,
|
||||
positions: Option<Box<[Vec3]>>,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct RawPosChannel {
|
||||
tx: Sender<Box<[Vec3]>>,
|
||||
rx: Receiver<Box<[Vec3]>>,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct IndexChannel {
|
||||
tx: Sender<(Box<[Vec3]>, Index)>,
|
||||
rx: Receiver<(Box<[Vec3]>, Index)>,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Toid {
|
||||
pub speed: f32,
|
||||
|
|
@ -48,28 +68,27 @@ pub struct LookAt(Vec3);
|
|||
|
||||
pub fn turkey_time(
|
||||
commands: &mut Commands,
|
||||
scene: &Handle<Scene>,
|
||||
r: &mut impl rand::prelude::Rng,
|
||||
assets: &AssetServer,
|
||||
) -> Entity {
|
||||
let speed_diff = r.gen_range(-SPEED_DIFF_RANGE..=SPEED_DIFF_RANGE);
|
||||
let speed_diff = r.random_range(-SPEED_DIFF_RANGE..=SPEED_DIFF_RANGE);
|
||||
let speed = SPEED + (SPEED * speed_diff);
|
||||
let vel = unit_vec(r) * speed;
|
||||
let buddies = r.gen_range(6..=8);
|
||||
let x = r.gen_range(-10.0..=10.0);
|
||||
let z = r.gen_range(-10.0..=10.0);
|
||||
let y = r.gen_range(0.1..=5.5);
|
||||
let spatial_bundle = SpatialBundle {
|
||||
transform: Transform::from_xyz(x, MIN_ALTITUDE + y, z),
|
||||
..Default::default()
|
||||
};
|
||||
let buddies = r.random_range(6..=8);
|
||||
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
|
||||
.spawn(spatial_bundle)
|
||||
.insert((Velocity(vel), Buddies::default(), Toid { speed, buddies }))
|
||||
.spawn((
|
||||
Velocity(vel),
|
||||
Buddies::default(),
|
||||
Toid { speed, buddies },
|
||||
Transform::from_xyz(x, y, z),
|
||||
))
|
||||
.with_children(|t| {
|
||||
t.spawn(SceneBundle {
|
||||
scene: scene.to_owned(),
|
||||
..Default::default()
|
||||
})
|
||||
t.spawn(WorldAssetRoot(
|
||||
assets.load(GltfAssetLabel::Scene(0).from_asset("models/toid.glb")),
|
||||
))
|
||||
.insert(Transform::default());
|
||||
// .insert(Transform::from_rotation(Quat::from_axis_angle(
|
||||
// Vec3::Y,
|
||||
|
|
@ -83,9 +102,9 @@ pub fn update_vel(
|
|||
mut toids: Query<(&Transform, &mut Velocity, &Toid, &Buddies, Entity)>,
|
||||
time: Res<Time>,
|
||||
positions: Res<Positions>,
|
||||
index: Res<NNTree>,
|
||||
index: Res<Naybs>,
|
||||
) {
|
||||
let dt = time.delta_seconds();
|
||||
let dt = time.delta_secs();
|
||||
let max_delta = MAX_DELTA_V * dt;
|
||||
for (xform, mut vel, toid, buddies, entity) in toids.iter_mut() {
|
||||
let speed = toid.speed;
|
||||
|
|
@ -107,11 +126,11 @@ pub fn update_vel(
|
|||
// avoid flying into neighbors
|
||||
let min_dist = speed;
|
||||
for neighbor in index
|
||||
.within_distance(pos, min_dist)
|
||||
.iter()
|
||||
.filter(|n| n.1.is_some() && n.1.unwrap() != entity)
|
||||
.index
|
||||
.locate_within_distance(pos.to_array(), min_dist)
|
||||
.filter(|n| n.data != entity)
|
||||
{
|
||||
let bp = neighbor.0;
|
||||
let bp = Vec3::from_array(*neighbor.geom());
|
||||
let bdir = pos - bp;
|
||||
let dist = bdir.length();
|
||||
let s = 1.0 - (dist / min_dist).min(1.0);
|
||||
|
|
@ -159,7 +178,7 @@ pub fn update_pos(
|
|||
time: Res<Time>,
|
||||
) {
|
||||
let mut new_look = Vec3::ZERO;
|
||||
let dt = time.delta_seconds();
|
||||
let dt = time.delta_secs();
|
||||
for (mut xform, vel, entity) in toids.iter_mut() {
|
||||
let look_at = xform.translation + vel.0;
|
||||
xform.translation += vel.0 * dt;
|
||||
|
|
@ -172,15 +191,15 @@ pub fn update_pos(
|
|||
|
||||
pub fn update_buddies(
|
||||
mut toids: Query<(&Transform, Entity, &Toid, &mut Buddies)>,
|
||||
index: Res<NNTree>,
|
||||
index: Res<Naybs>,
|
||||
positions: Res<Positions>,
|
||||
) {
|
||||
let d2 = (BUDDY_RADIUS * 1.5).powi(2);
|
||||
for (xform, entity, toid, mut buddies) in toids.iter_mut() {
|
||||
let pos = xform.translation;
|
||||
for buddy in buddies.clone().iter() {
|
||||
let bp = positions.get(buddy).unwrap();
|
||||
let bd2 = (*bp - pos).length_squared();
|
||||
let bp = positions[buddy];
|
||||
let bd2 = (bp - pos).length_squared();
|
||||
if bd2 > d2 {
|
||||
buddies.remove(buddy);
|
||||
}
|
||||
|
|
@ -188,12 +207,13 @@ pub fn update_buddies(
|
|||
|
||||
if buddies.len() < toid.buddies {
|
||||
let diff = toid.buddies - buddies.len();
|
||||
for (_, neighbor) in index
|
||||
.k_nearest_neighbour(pos, diff + 1)
|
||||
.into_iter()
|
||||
.filter(|n| n.1.is_some() && n.1.unwrap() != entity)
|
||||
for neighbor in index
|
||||
.index
|
||||
.nearest_neighbor_iter(pos.to_array())
|
||||
.filter(|n| n.data != entity)
|
||||
.take(diff)
|
||||
{
|
||||
buddies.insert(neighbor.unwrap());
|
||||
buddies.insert(neighbor.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -202,41 +222,42 @@ pub fn update_buddies(
|
|||
pub fn update_gizmos(toids: Query<&Transform, With<Toid>>, mut gizmos: Gizmos) {
|
||||
for toid in toids.iter() {
|
||||
let pos = toid.translation;
|
||||
let up = toid.up();
|
||||
let forward = toid.forward();
|
||||
let right = toid.right();
|
||||
gizmos.ray(pos, pos + forward, Color::RED);
|
||||
gizmos.ray(pos, pos + up, Color::BLUE);
|
||||
gizmos.ray(pos, pos + right, Color::GREEN);
|
||||
let up = toid.up().as_vec3();
|
||||
let forward = toid.forward().as_vec3();
|
||||
let right = toid.right().as_vec3();
|
||||
gizmos.ray(pos, pos + forward, Color::srgb(1., 0., 0.));
|
||||
gizmos.ray(pos, pos + up, Color::srgb(0., 1., 0.));
|
||||
gizmos.ray(pos, pos + right, Color::srgb(0., 0., 1.));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rotate_camera(
|
||||
mut query: Query<&mut Transform, With<Camera>>,
|
||||
keys: Res<Input<KeyCode>>,
|
||||
keys: Res<ButtonInput<Key>>,
|
||||
lookat: Res<LookAt>,
|
||||
) {
|
||||
let mut xform = query.single_mut();
|
||||
let mut xform = query.single_mut().unwrap();
|
||||
|
||||
let forward = xform.forward() * 0.7;
|
||||
let right = xform.right();
|
||||
let forward = xform.forward().as_vec3() * 0.7;
|
||||
let right = xform.right().as_vec3();
|
||||
|
||||
if keys.pressed(KeyCode::Right) {
|
||||
if keys.pressed(Key::ArrowRight) {
|
||||
xform.translation += right;
|
||||
}
|
||||
if keys.pressed(KeyCode::Left) {
|
||||
if keys.pressed(Key::ArrowLeft) {
|
||||
xform.translation -= right;
|
||||
}
|
||||
|
||||
if keys.pressed(KeyCode::Up) {
|
||||
if keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight) {
|
||||
if keys.pressed(Key::ArrowUp) {
|
||||
if keys.pressed(Key::Shift) {
|
||||
xform.translation += Vec3::Y;
|
||||
} else {
|
||||
xform.translation += forward;
|
||||
}
|
||||
}
|
||||
if keys.pressed(KeyCode::Down) {
|
||||
if keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight) {
|
||||
|
||||
if keys.pressed(Key::ArrowDown) {
|
||||
if keys.pressed(Key::Shift) {
|
||||
xform.translation -= Vec3::Y;
|
||||
} else {
|
||||
xform.translation -= forward;
|
||||
|
|
@ -253,10 +274,10 @@ pub fn rotate_camera(
|
|||
pub fn unit_vec(r: &mut impl rand::Rng) -> Vec3 {
|
||||
let mut x1: f32 = 0.0;
|
||||
let mut x2: f32 = 0.0;
|
||||
let mut ssum = std::f32::MAX;
|
||||
let mut ssum = f32::MAX;
|
||||
while ssum >= 1.0 {
|
||||
x1 = r.gen_range(-1.0..=1.0);
|
||||
x2 = r.gen_range(-1.0..=1.0);
|
||||
x1 = r.random_range(-1.0..=1.0);
|
||||
x2 = r.random_range(-1.0..=1.0);
|
||||
ssum = x1.powi(2) + x2.powi(2);
|
||||
}
|
||||
let sqrt = (1.0 - ssum).sqrt();
|
||||
|
|
|
|||
86
src/main.rs
86
src/main.rs
|
|
@ -1,34 +1,34 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use audubon::*;
|
||||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
input::keyboard::Key,
|
||||
prelude::*,
|
||||
};
|
||||
use bevy_spatial::{AutomaticUpdate, TransformMode};
|
||||
use toids::{
|
||||
rotate_camera, turkey_time, update_buddies, update_pos, update_vel, Config, LookAt, Positions,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let config: audubon::Config = argh::from_env();
|
||||
let config: toids::Config = argh::from_env();
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugins((
|
||||
AutomaticUpdate::<Toid>::new()
|
||||
.with_transform(TransformMode::GlobalTransform)
|
||||
.with_frequency(Duration::from_millis(150)),
|
||||
FrameTimeDiagnosticsPlugin,
|
||||
FrameTimeDiagnosticsPlugin::default(),
|
||||
LogDiagnosticsPlugin::default(),
|
||||
))
|
||||
.insert_resource(config)
|
||||
.insert_resource(Positions::default())
|
||||
.insert_resource(ClearColor(Color::rgb(0.64, 0.745, 0.937))) // a nice light blue
|
||||
.insert_resource(AmbientLight {
|
||||
.insert_resource(ClearColor(Color::srgb(0.64, 0.745, 0.937))) // a nice light blue
|
||||
.insert_resource(GlobalAmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 1.0,
|
||||
..Default::default()
|
||||
})
|
||||
.insert_resource(LookAt::default())
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (update_pos, update_buddies, update_vel).chain())
|
||||
.add_systems(Update, (rotate_camera, bevy::window::close_on_esc))
|
||||
.add_systems(Update, (rotate_camera, close_on_esc))
|
||||
//.add_systems(Update, update_gizmos)
|
||||
.run();
|
||||
}
|
||||
|
|
@ -40,39 +40,55 @@ fn setup(
|
|||
config: Res<Config>,
|
||||
models: Res<AssetServer>,
|
||||
) {
|
||||
let rand = &mut rand::thread_rng();
|
||||
let rand = &mut rand::rng();
|
||||
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(0., 5.0, 25.).looking_at(Vec3::new(0.0, 5.0, 0.0), Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
commands.spawn((
|
||||
Transform::from_xyz(0., 5.0, 25.).looking_at(Vec3::new(0.0, 5.0, 0.0), Vec3::Y),
|
||||
Camera3d::default(),
|
||||
));
|
||||
// plane
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane::from_size(500.0))),
|
||||
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||
..default()
|
||||
});
|
||||
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))),
|
||||
Transform::default(),
|
||||
));
|
||||
|
||||
let toid_model = models.load("models/boid.glb#Scene0");
|
||||
// let toid_model = models.load("models/boid.glb#Scene0");
|
||||
|
||||
for _ in 0..config.toids {
|
||||
let _ = turkey_time(&mut commands, &toid_model, rand);
|
||||
let _ = turkey_time(&mut commands, rand, &models);
|
||||
}
|
||||
|
||||
// instructions
|
||||
commands.spawn(
|
||||
TextBundle::from_section(
|
||||
"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.",
|
||||
TextStyle {
|
||||
font_size: 20.,
|
||||
commands.spawn((
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: px(12.0),
|
||||
left: px(12.0),
|
||||
..default()
|
||||
},
|
||||
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 'ESC' to quit."),
|
||||
TextFont {
|
||||
font_size: FontSize::Px(20.0),
|
||||
..default()
|
||||
},
|
||||
)
|
||||
.with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
)],
|
||||
));
|
||||
}
|
||||
|
||||
fn close_on_esc(
|
||||
mut commands: Commands,
|
||||
focused_windows: Query<(Entity, &Window)>,
|
||||
input: Res<ButtonInput<Key>>,
|
||||
) {
|
||||
for (window, focus) in focused_windows.iter() {
|
||||
if !focus.focused {
|
||||
continue;
|
||||
}
|
||||
|
||||
if input.just_pressed(Key::Escape) {
|
||||
commands.entity(window).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue