From a390334efb27b9fcabc9188b17ee328185e52688 Mon Sep 17 00:00:00 2001 From: John Peel Date: Thu, 31 Dec 2020 14:43:34 -0500 Subject: [PATCH] Updated to bevy 0.4 and added webgl/wasm support. --- .cargo/config | 3 - .cargo/config.toml | 20 ++ .vscode/settings.json | 12 + Cargo.toml | 22 +- Makefile.toml | 44 ++++ README.md | 8 +- index.html | 12 + src/fps_plugin.rs | 38 --- src/main.rs | 332 +++--------------------- src/plugins/bidimensional/flock.rs | 177 +++++++++++++ src/plugins/bidimensional/mod.rs | 6 + src/plugins/bidimensional/velocity.rs | 67 +++++ src/plugins/examples/mod.rs | 4 + src/plugins/examples/simple_flocking.rs | 71 +++++ src/plugins/fps.rs | 63 +++++ src/plugins/mod.rs | 6 + src/util.rs | 44 ++++ 17 files changed, 585 insertions(+), 344 deletions(-) delete mode 100644 .cargo/config create mode 100644 .cargo/config.toml create mode 100644 .vscode/settings.json create mode 100644 Makefile.toml create mode 100644 index.html delete mode 100644 src/fps_plugin.rs create mode 100644 src/plugins/bidimensional/flock.rs create mode 100644 src/plugins/bidimensional/mod.rs create mode 100644 src/plugins/bidimensional/velocity.rs create mode 100644 src/plugins/examples/mod.rs create mode 100644 src/plugins/examples/simple_flocking.rs create mode 100644 src/plugins/fps.rs create mode 100644 src/plugins/mod.rs create mode 100644 src/util.rs diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index ee97b07..0000000 --- a/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[target.x86_64-pc-windows-msvc] -linker = "lld-link.exe" -rustflags = ["-Clinker=lld", "-Zshare-generics=y"] \ No newline at end of file diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..33a564e --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,20 @@ +# NOTE: For maximum performance, build using a nightly compiler +# If you are using rust stable, remove the "-Zshare-generics=y" below. + +[target.x86_64-unknown-linux-gnu] +linker = "/usr/bin/clang" +rustflags = ["-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"] + +# NOTE: you must manually install https://github.com/michaeleisel/zld on mac. you can easily do this with the "brew" package manager: +# `brew install michaeleisel/zld/zld` +[target.x86_64-apple-darwin] +rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld", "-Zshare-generics=y", "-Zrun-dsymutil=no"] + +[target.x86_64-pc-windows-msvc] +linker = "rust-lld.exe" +rustflags = ["-Zshare-generics=y"] + +# Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only' +# In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains. +#[profile.dev] +#debug = 1 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2787f9b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "cSpell.words": [ + "Boid", + "Inconsolata", + "bidimensional", + "boids", + "spacefield", + "vsync", + "wasm", + "webgl" + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index a9f67d7..95a7be2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,25 @@ [package] name = "bevy_test" -version = "0.1.0" +version = "0.1.1" authors = ["John Peel "] edition = "2018" +[features] +default = [ + "bevy/bevy_winit", + "bevy/render", + "bevy/png", +] + +native = [ + "bevy/bevy_wgpu", +] + +web = [ + "bevy_webgl2" +] + [dependencies] -bevy = "0.1.3" -rand = "0.7" +bevy = { version = "0.4", default-features = false } +bevy_webgl2 = { version = "0.4", optional = true } +rand = "0.8" diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 0000000..4e38178 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,44 @@ +[env] +ENV_DIR = {source="${CARGO_MAKE_PROFILE}", default_value = "debug", mapping = {release = "release"}} +CARGO_TARGET_DIR = {value = "target", condition = {env_not_set = ["CARGO_TARGET_DIR"]}} +CARGO_WASM_PATH = "${CARGO_TARGET_DIR}/wasm32-unknown-unknown/${ENV_DIR}/${CARGO_MAKE_CRATE_NAME}.wasm" +CARGO_PROFILE = "dev" +TARGET_DIR = "target" +CARGO_MANIFEST_DIR = "${CARGO_MAKE_WORKING_DIRECTORY}" + +[env.release] +CARGO_RELEASE_ARGS = "--release" + +[tasks.show-env] +command = "env" + +[tasks.basic-http-server] +install_crate = {crate_name = "basic-http-server", binary = "basic-http-server", test_arg="--help"} + +[tasks.wasm-bindgen-cli] +install_crate = {crate_name = "wasm-bindgen-cli", binary = "wasm-bindgen", test_arg="--help"} + +[tasks.cargo-build-web] +args = ["build", "--target", "wasm32-unknown-unknown", "--features", "web", "@@split(CARGO_RELEASE_ARGS, )"] +command = "cargo" + +[tasks.build-web] +args = ["--out-dir", "${TARGET_DIR}", "--out-name", "wasm", "--target", "web", "--no-typescript", "${CARGO_WASM_PATH}"] +command = "wasm-bindgen" +dependencies = ["cargo-build-web", "wasm-bindgen-cli"] + +[tasks.build-native] +args = ["build", "--features", "native", "@@split(CARGO_RELEASE_ARGS, )"] +command = "cargo" + +[tasks.run] +command = "${CARGO_TARGET_DIR}/${ENV_DIR}/${CARGO_MAKE_BINARY_EXECUTABLE_NAME}" +dependencies = ["build-native"] + +[tasks.serve] +command = "basic-http-server" +args = ["-x"] +dependencies = ["build-web", "basic-http-server"] + +[tasks.test] +disabled = true \ No newline at end of file diff --git a/README.md b/README.md index 80f7fac..cb3757c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ Steering behaviors flocking example using bevy & Rust. -This repo is setup for Fast Compiles with bevy. See: https://bevyengine.org/learn/book/getting-started/setup/#enable-fast-compiles-optional \ No newline at end of file +This repo is setup for Fast Compiles with bevy. See: https://bevyengine.org/learn/book/getting-started/setup/#enable-fast-compiles-optional + +You will need cargo-make to run any of the following commands. +It can be installed with `cargo install cargo-make` + +Run native with: `cargo make run` +Run wasm/webgl with: `cargo make serve` diff --git a/index.html b/index.html new file mode 100644 index 0000000..e640612 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + Flocking Example + + + + + diff --git a/src/fps_plugin.rs b/src/fps_plugin.rs deleted file mode 100644 index 0fb6571..0000000 --- a/src/fps_plugin.rs +++ /dev/null @@ -1,38 +0,0 @@ - -use bevy::{prelude::*, diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin}, ecs::Mut}; - -pub struct OnscreenFpsPlugin; - -impl Plugin for OnscreenFpsPlugin { - fn build(&self, app: &mut AppBuilder) { - app - .add_plugin(FrameTimeDiagnosticsPlugin) - .add_startup_system(Self::setup_system.system()) - .add_system(Self::fps_update.system()); - } -} - -impl OnscreenFpsPlugin { - fn fps_update(diagnostics: Res, mut text: Mut) { - if let Some((Some(fps), Some(average))) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS).map(|x| (x.value(), x.average())) { - text.value = format!("{:<3.3} ({:<3.3})", fps, average); - } - } - - fn setup_system(mut commands: Commands, asset_server: Res) { - commands - .spawn(UiCameraComponents::default()) - .spawn(TextComponents { - text: Text { - value: "FPS".to_string(), - font: asset_server.load("assets/fonts/Inconsolata.ttf").unwrap(), - style: TextStyle { - font_size: 25.0, - color: Color::WHITE, - }, - }, - transform: Transform::new(Mat4::from_translation(Vec3::new(0.0, 0.0, 2.0))), - ..Default::default() - }); - } -} diff --git a/src/main.rs b/src/main.rs index 635b3dd..659a21f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,312 +1,46 @@ -use rand::Rng; use bevy::prelude::*; -mod fps_plugin; +mod util; +mod plugins; -#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] -struct Boid { - id: usize, - flock_id: usize, - velocity: Vec3, - max_speed: f32, - safe_radius: f32 -} - -#[derive(Debug, Clone)] -struct FlockingPlugin { - flocks: Vec -} - -#[derive(Debug, Copy, Clone)] -struct FlockParameters { - id: usize, - boid_count: usize, - color: Color, - flock_radius: f32, - alignment_strength: f32, - cohesion_strength: f32, - separation_strength: f32 -} - -struct FlockAverages { - average_position: Vec3, - average_forward: Vec3, - current_count: Option, - boids: Vec<(Boid, Vec3)> -} - -impl Plugin for FlockingPlugin { - fn build(&self, app: &mut AppBuilder) { - app - .add_resource(self.flocks.clone()) - .add_startup_system(Self::setup_system.system()) - .add_system(Self::update_flocks.system()) - .add_stage_after("update", "movement") - .add_system_to_stage("movement", Self::movement_system.system()); - } -} - -impl FlockingPlugin { - fn calculate_alignment(max_speed: f32, average_forward: Vec3) -> Vec3 { - let mut alignment = average_forward / max_speed; - - if alignment.length_squared() > 1.0 { - alignment = alignment.normalize(); - } - - alignment - } - - fn calculate_cohesion(position: Vec3, average_position: Vec3, flock_radius: f32) -> Vec3 { - let mut cohesion = average_position - position; - - if cohesion.length_squared() < flock_radius * flock_radius { - cohesion /= flock_radius; - } else { - cohesion = cohesion.normalize(); - } - - cohesion - } - - fn calculate_separation(boid: Boid, position: Vec3, boids: &[(Boid, Vec3)]) -> Vec3 { - let mut separation = Vec3::zero(); - - for (other, other_pos) in boids.iter() { - if boid.id != other.id { - let difference = position - *other_pos; - let distance_squared = difference.length_squared(); - let minimum_distance = boid.safe_radius + other.safe_radius; - - if distance_squared < minimum_distance * minimum_distance { - separation += difference.normalize() * (minimum_distance - distance_squared.sqrt()) / minimum_distance; - } - } - } - - if separation.length_squared() > 1.0 { - separation = separation.normalize(); - } - - separation - } - - fn normalize_pos_to(position: Vec3, center: Vec3, width: f32, height: f32) -> Vec3 { - let mut new_position = position; - if position.x() < center.x() - width { - new_position.set_x(position.x() + 2.0 * width); - } else if position.x() > center.x() + width { - new_position.set_x(position.x() - 2.0 * width); - } - - if position.y() < center.y() - height { - new_position.set_y(position.y() + 2.0 * height); - } else if position.y() > center.y() + height { - new_position.set_y(position.y() - 2.0 * height); - } - - new_position - } - - fn calculate_heading(forward: Vec3) -> f32 { - let mut heading = 0.0; - if forward.x() != 0.0 || forward.y() != 0.0 { - let normalized_forward = forward.normalize(); - - if normalized_forward.y() < 0.0 { - heading = -normalized_forward.x().acos(); - } else { - heading = normalized_forward.x().acos(); - } - - if heading.is_nan() || heading.is_infinite() { - heading = 0.0; - } - } - heading - } - - fn calculate_averages(params: Vec, query: &mut Query<(&mut Boid, &Translation)>, width: f32, height: f32) -> Vec { - let mut result = Vec::::with_capacity(params.len()); - - for flock in params.iter() { - result.insert(flock.id, FlockAverages { - average_position: Vec3::zero(), - average_forward: Vec3::zero(), - current_count: None, - boids: Vec::with_capacity(flock.boid_count) - }); - } - - for (boid, position) in &mut query.iter() { - let mut current_average = result[boid.flock_id].average_position; - if let Some(current_count) = result[boid.flock_id].current_count { - current_average = Self::normalize_pos_to(current_average / current_count as f32, Vec3::zero(), width, height); - } - - let position = Self::normalize_pos_to(position.0, current_average, width, height); - - result[boid.flock_id].average_position += position; - result[boid.flock_id].average_forward += boid.velocity; - result[boid.flock_id].boids.push((*boid, position)); - result[boid.flock_id].current_count = Some(result[boid.flock_id].current_count.map_or_else(|| 0, |x| x + 1)); - } - - for flock in params.iter() { - result[flock.id].average_position /= flock.boid_count as f32; - result[flock.id].average_forward /= flock.boid_count as f32; - - let average_position = result[flock.id].average_position; - for (_boid, position) in &mut result[flock.id].boids { - *position = Self::normalize_pos_to(*position, average_position, width, height); - } - } - - result - } - - fn setup_system(mut commands: Commands, window: Res, params: Res>, asset_server: Res, mut materials: ResMut>) { - commands - .spawn(Camera2dComponents::default()); - - commands - .spawn(SpriteComponents { - material: materials.add(ColorMaterial { - texture: Some(asset_server.load("assets/sprite/spacefield.png").unwrap()), - color: Color::default() - }), - translation: Translation::new(0.0, 0.0, 0.0), - sprite: Sprite { - size: Vec2::new(window.width as f32, window.height as f32), - ..Default::default() - }, - ..Default::default() - }); - - let ship_handle = asset_server.load("assets/sprite/ship.png").unwrap(); - let mut rng = rand::thread_rng(); - for flock in params.iter() { - let material = materials.add(ColorMaterial { - texture: Some(ship_handle), - color: flock.color - }); - - for id in 0..flock.boid_count { - commands - .spawn(SpriteComponents { - material, - translation: Translation::new( - rng.gen_range(-300.0, 300.0), - rng.gen_range(-300.0, 300.0), - id as f32 - ), - sprite: Sprite { - size: Vec2::new(8.0, 8.0), - }, - ..Default::default() - }) - .with(Boid { id, flock_id: flock.id, velocity: Vec3::zero(), max_speed: 200.0, safe_radius: 50.0 }); - } - } - } - - fn update_flocks(time: Res