Skip to content

Commit

Permalink
Parallel queries and fixes (#3)
Browse files Browse the repository at this point in the history
Parallel queries
Time step management fixed
Improved CI
  • Loading branch information
ManevilleF authored Oct 15, 2021
1 parent 0b7165e commit a0b7bc2
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 52 deletions.
39 changes: 36 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,38 @@ jobs:
- name: build
run: cargo build --verbose

build_features:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: all features
run: cargo build --verbose --all-features
- name: no default features
run: cargo build --verbose --no-default-features
- name: debug
run: cargo build --verbose --no-default-features --features debug

build_examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: build
run: cargo build --examples
- name: build 2d cloth
run: cargo clippy --all-features --example 2d_cloth
- name: build 2d cloth cutter
run: cargo clippy --all-features --example 2d_cloth_cutter
- name: build 2d line
run: cargo clippy --all-features --example 2d_line
- name: build 3d line
run: cargo clippy --all-features --example 3d_line
- name: build 3d cloth
run: cargo clippy --all-features --example 3d_cloth

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: test
run: cargo test --tests --all-features

fmt:
runs-on: ubuntu-latest
Expand All @@ -38,4 +64,11 @@ jobs:
- name: Cargo clippy installation
run: rustup component add clippy
- name: Cargo clippy check
run: cargo clippy --all-targets -- -D warnings
run: cargo clippy --all-features --all --tests -- -D warnings

rustdoc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: rustdoc
run: cargo rustdoc --all-features
10 changes: 3 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
name = "bevy_verlet"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
authors = ["Felix de Maneville <felix.maneville@gmail.com>"]
repository = "https://github.com/ManevilleF/bevy_verlet"
license-file = "./LICENSE"

[features]
default = []
debug = ["bevy_prototype_debug_lines"]
shuffle = ["rand"]

[dependencies]

Expand All @@ -20,10 +20,6 @@ default-features = false
version = "0.3"
optional = true

[dependencies.rand]
version = "0.8.4"
optional = true

[dev-dependencies]

[dev-dependencies.bevy]
Expand Down
32 changes: 27 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod components;
mod resources;
mod systems;

use crate::verlet_time_step::VerletTimeStep;
use bevy::core::FixedTimestep;
use bevy::prelude::*;
#[cfg(feature = "debug")]
Expand All @@ -18,25 +19,46 @@ pub struct BevyVerletPlugin {
impl Plugin for BevyVerletPlugin {
fn build(&self, app: &mut AppBuilder) {
let system_set = SystemSet::new()
.with_system(systems::points::update_points.system().label("points"))
.with_system(systems::sticks::update_sticks.system())
.with_system(systems::sticks::handle_stick_constraints.system());
.with_system(
systems::points::update_points
.system()
.label("points")
.after("sticks"),
)
.with_system(systems::sticks::update_sticks.system().label("sticks"))
.with_system(
systems::sticks::handle_stick_constraints
.system()
.after("sticks"),
);
let system_set = if let Some(step) = self.time_step {
app.insert_resource(VerletTimeStep::FixedDeltaTime(step));
system_set.with_run_criteria(FixedTimestep::step(step))
} else {
app.insert_resource(VerletTimeStep::DeltaTime);
system_set
};
app.add_system_set(system_set);
#[cfg(feature = "debug")]
{
app.add_plugin(DebugLinesPlugin);
app.add_system(systems::sticks::debug_draw_sticks.system().after("points"));
app.add_system(systems::sticks::debug_draw_sticks.system().after("sticks"));
}
}
}

impl Default for BevyVerletPlugin {
fn default() -> Self {
Self { time_step: None }
Self {
time_step: Some(0.02),
}
}
}

impl BevyVerletPlugin {
pub fn new(time_step: f64) -> Self {
Self {
time_step: Some(time_step),
}
}
}
7 changes: 5 additions & 2 deletions src/resources/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ pub struct VerletConfig {
/// Sets the number of sticks computation iteration.
/// The higher the value, the more precision and less elasticity for the sticks but the cost is increased
pub sticks_computation_depth: u8,
/// Enables parallel computing for sticks and points, setting the parallel batch size
pub parallel_processing_batch_size: Option<usize>,
}

impl Default for VerletConfig {
fn default() -> Self {
Self {
gravity: Vec3::new(0., -9.81, 0.),
friction: 0.01,
sticks_computation_depth: 1,
friction: 0.02,
sticks_computation_depth: 2,
parallel_processing_batch_size: None,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/resources/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub use config::*;

mod config;
pub(crate) mod verlet_time_step;
5 changes: 5 additions & 0 deletions src/resources/verlet_time_step.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[derive(Debug, Clone)]
pub enum VerletTimeStep {
DeltaTime,
FixedDeltaTime(f64),
}
44 changes: 34 additions & 10 deletions src/systems/points.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
use crate::components::{VerletLocked, VerletPoint};
use crate::resources::verlet_time_step::VerletTimeStep;
use crate::resources::VerletConfig;
use bevy::prelude::*;
use bevy::tasks::ComputeTaskPool;
use std::ops::Deref;

fn update_point(
transform: &mut Transform,
point: &mut VerletPoint,
down_force: Vec3,
friction: f32,
) {
let position = transform.translation;
let velocity = if let Some(pos) = point.old_position {
position - pos
} else {
Vec3::ZERO
};
transform.translation += velocity * friction + down_force;
point.old_position = Some(position);
}

pub fn update_points(
time_step: Res<VerletTimeStep>,
mut points_query: Query<(&mut Transform, &mut VerletPoint), Without<VerletLocked>>,
pool: Res<ComputeTaskPool>,
time: Res<Time>,
config: Option<Res<VerletConfig>>,
) {
let config = config.map(|g| *g).unwrap_or_default();
let delta_time = time.delta_seconds();
let delta_time = match time_step.deref() {
VerletTimeStep::DeltaTime => time.delta_seconds(),
VerletTimeStep::FixedDeltaTime(dt) => *dt as f32,
};
let down_force = config.gravity * delta_time;
for (mut transform, mut point) in points_query.iter_mut() {
let position = transform.translation;
let velocity = if let Some(pos) = point.old_position {
position - pos
} else {
Vec3::ZERO
};
transform.translation += velocity * config.friction_coefficient() + down_force;
point.old_position = Some(position);
let friction = config.friction_coefficient();
if let Some(batch_size) = config.parallel_processing_batch_size {
points_query.par_for_each_mut(&pool, batch_size, |(mut transform, mut point)| {
update_point(&mut transform, &mut point, down_force, friction);
});
} else {
for (mut transform, mut point) in points_query.iter_mut() {
update_point(&mut transform, &mut point, down_force, friction);
}
}
}
76 changes: 51 additions & 25 deletions src/systems/sticks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use crate::components::{VerletLocked, VerletPoint, VerletStick};
use crate::{VerletConfig, VerletStickMaxTension};
use bevy::log;
use bevy::prelude::*;
use bevy::tasks::ComputeTaskPool;
#[cfg(feature = "debug")]
use bevy_prototype_debug_lines::DebugLines;
#[cfg(feature = "shuffle")]
use rand::{prelude::SliceRandom, thread_rng};
use std::sync::RwLock;

#[allow(clippy::type_complexity)]
pub fn update_sticks(
Expand All @@ -17,14 +17,8 @@ pub fn update_sticks(
)>,
) {
let config = config.map(|g| *g).unwrap_or_default();
#[cfg(not(feature = "shuffle"))]
let iterator = sticks_query;
#[cfg(feature = "shuffle")]
let mut iterator: Vec<&VerletStick> = sticks_query.iter().collect();
for _ in 0..=config.sticks_computation_depth {
#[cfg(feature = "shuffle")]
iterator.shuffle(&mut thread_rng());
for stick in iterator.iter() {
for stick in sticks_query.iter() {
let (point_a, point_a_locked) = match points_query.q0().get(stick.point_a_entity) {
Ok(p) => p,
Err(e) => {
Expand Down Expand Up @@ -60,29 +54,61 @@ pub fn update_sticks(
}
}

fn handle_stick_constraint(
entity: Entity,
stick: &VerletStick,
max_tension: &VerletStickMaxTension,
points_query: &Query<&Transform, With<VerletPoint>>,
) -> Option<Entity> {
let point_a = match points_query.get(stick.point_a_entity) {
Ok(p) => p,
Err(e) => {
log::error!("Could not find point_a entity for stick: {}", e);
return None;
}
};
let point_b = match points_query.get(stick.point_b_entity) {
Ok(p) => p,
Err(e) => {
log::error!("Could not find point_b entity for stick: {}", e);
return None;
}
};
let distance = point_a.translation.distance(point_b.translation);
if distance > stick.length * max_tension.0 {
Some(entity)
} else {
None
}
}

pub fn handle_stick_constraints(
pool: Res<ComputeTaskPool>,
mut commands: Commands,
sticks_query: Query<(Entity, &VerletStick, &VerletStickMaxTension)>,
points_query: Query<&Transform, With<VerletPoint>>,
config: Option<Res<VerletConfig>>,
) {
for (entity, stick, max_tension) in sticks_query.iter() {
let point_a = match points_query.get(stick.point_a_entity) {
Ok(p) => p,
Err(e) => {
log::error!("Could not find point_a entity for stick: {}", e);
continue;
let config = config.map(|g| *g).unwrap_or_default();
if let Some(batch_size) = config.parallel_processing_batch_size {
let sticks_to_destroy = RwLock::new(Vec::new());
sticks_query.par_for_each(&pool, batch_size, |(entity, stick, max_tension)| {
if let Some(entity) = handle_stick_constraint(entity, stick, max_tension, &points_query)
{
let mut lock = sticks_to_destroy.write().unwrap();
lock.push(entity);
}
};
let point_b = match points_query.get(stick.point_b_entity) {
Ok(p) => p,
Err(e) => {
log::error!("Could not find point_b entity for stick: {}", e);
continue;
});
let lock = sticks_to_destroy.read().unwrap();
for entity in lock.iter() {
commands.entity(*entity).despawn_recursive();
}
} else {
for (entity, stick, max_tension) in sticks_query.iter() {
if let Some(entity) = handle_stick_constraint(entity, stick, max_tension, &points_query)
{
commands.entity(entity).despawn_recursive();
}
};
let distance = point_a.translation.distance(point_b.translation);
if distance > stick.length * max_tension.0 {
commands.entity(entity).despawn_recursive();
}
}
}
Expand Down

0 comments on commit a0b7bc2

Please sign in to comment.