diff --git a/examples/2d_cloth.rs b/examples/2d_cloth.rs index 2a2d4f8..953ec21 100644 --- a/examples/2d_cloth.rs +++ b/examples/2d_cloth.rs @@ -38,7 +38,7 @@ fn setup(mut commands: Commands) { ), ..Default::default() }, - VerletPoint::default(), + VerletPoint::new(0.2), Name::new(format!("Point {}", i)), )); if j == 0 { diff --git a/examples/2d_cloth_cutter.rs b/examples/2d_cloth_cutter.rs index 051b9ad..812b8cc 100644 --- a/examples/2d_cloth_cutter.rs +++ b/examples/2d_cloth_cutter.rs @@ -1,6 +1,8 @@ use bevy::{math::Vec3Swizzles, prelude::*, window::PrimaryWindow}; use bevy_verlet::prelude::*; +const MOUSE_RADIUS: f32 = 20.0; + fn main() { App::new() .add_plugins(DefaultPlugins.set(WindowPlugin { @@ -13,7 +15,7 @@ fn main() { })) .add_plugins(VerletPlugin::default()) .add_systems(Startup, setup) - .add_systems(Update, cut_sticks) + .add_systems(Update, (drag_points, cut_sticks)) .run(); } @@ -31,7 +33,7 @@ fn setup(mut commands: Commands) { origin_y + (-10. * j as f32), 0., )), - VerletPoint::default(), + VerletPoint::new(0.1), Name::new(format!("Point {}", i)), )); if j == 0 && i % 2 == 0 { @@ -67,7 +69,7 @@ fn spawn_stick( point_b_entity: *other_entity, length, }, - VerletStickMaxTension(5.), + VerletStickMaxTension(3.), )); } } @@ -86,7 +88,7 @@ fn cut_sticks( mouse_input: Res>, windows: Query<&Window, With>, ) { - if !mouse_input.pressed(MouseButton::Left) { + if !mouse_input.pressed(MouseButton::Right) { return; } let window = windows.single(); @@ -94,7 +96,6 @@ fn cut_sticks( None => return, Some(p) => mouse_coords(window, p), }; - let l = 20.; for (entity, stick) in sticks.iter() { let [a, b] = points .get_many(stick.entities()) @@ -102,8 +103,45 @@ fn cut_sticks( .unwrap(); let distance_a = p.distance(a); let distance_b = p.distance(b); - if distance_a > 0. && distance_a <= l && distance_b > 0. && distance_b <= l { + if distance_a > 0. + && distance_a <= MOUSE_RADIUS + && distance_b > 0. + && distance_b <= MOUSE_RADIUS + { commands.entity(entity).despawn_recursive(); } } } + +fn drag_points( + mut points: Query<(Entity, &mut Transform), With>, + mouse_input: Res>, + windows: Query<&Window, With>, + mut dragged: Local>, +) { + if !mouse_input.pressed(MouseButton::Left) { + *dragged = vec![]; + return; + } + let window = windows.single(); + let p = match window.cursor_position() { + None => return, + Some(p) => mouse_coords(window, p), + }; + if dragged.is_empty() { + *dragged = points + .iter() + .filter_map(|(e, tr)| { + let point = tr.translation.xy(); + let dist = point.distance(p); + (dist <= MOUSE_RADIUS).then_some(e) + }) + .collect(); + return; + }; + let mut iter = points.iter_many_mut(&*dragged); + while let Some((_, mut tr)) = iter.fetch_next() { + tr.translation.x = p.x; + tr.translation.y = p.y; + } +} diff --git a/examples/2d_line.rs b/examples/2d_line.rs index 92e67bb..574e93e 100644 --- a/examples/2d_line.rs +++ b/examples/2d_line.rs @@ -27,7 +27,7 @@ fn setup_free_line(mut commands: Commands) { for i in 0..=points_count { let mut cmd = commands.spawn(( sprite_bundle(Color::WHITE, Vec2::new(50. * i as f32, 300.)), - VerletPoint::default(), + VerletPoint::new(0.1), Name::new(format!("Point {}", i)), )); if previous_entity.is_none() { diff --git a/examples/3d_cloth.rs b/examples/3d_cloth.rs index 18756fb..4d05da0 100644 --- a/examples/3d_cloth.rs +++ b/examples/3d_cloth.rs @@ -34,7 +34,7 @@ fn setup( let mesh = meshes.add(Cuboid::new(1., 1., 1.)); let stick_length: f32 = 2.; let (origin_x, origin_y) = (-5., 10.); - let (points_x_count, points_y_count) = (20, 15); + let (points_x_count, points_y_count) = (30, 25); let mut entities = Vec::new(); for j in 0..points_y_count { for i in 0..points_x_count { diff --git a/src/components/point.rs b/src/components/point.rs index 3fb05a4..b0540e6 100644 --- a/src/components/point.rs +++ b/src/components/point.rs @@ -2,7 +2,35 @@ use bevy::prelude::{Component, Reflect, Vec3}; /// Main verlet physics component. /// Any entity with this component will have physics applied to it -#[derive(Debug, Clone, Component, Default, Reflect)] +#[derive(Debug, Clone, Component, Reflect)] pub struct VerletPoint { pub(crate) old_position: Option, + /// Point mass, defaults to 1.0 + pub mass: f32, +} + +impl Default for VerletPoint { + fn default() -> Self { + Self { + old_position: None, + mass: 1.0, + } + } +} + +impl VerletPoint { + /// Creates a point with a custom mass value + /// + /// # Panics + /// + /// Panics if `mass` is lesser or equal to zero + #[inline] + #[must_use] + pub fn new(mass: f32) -> Self { + assert!(mass > 0.0); + Self { + mass, + old_position: None, + } + } } diff --git a/src/config.rs b/src/config.rs index 3696a1e..036bf82 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,7 +25,7 @@ impl Default for VerletConfig { fn default() -> Self { Self { gravity: Vec3::new(0., -9.81, 0.), - friction: 0.02, + friction: 0.01, sticks_computation_depth: 5, parallel_processing: true, } diff --git a/src/systems/points.rs b/src/systems/points.rs index dc895b7..4b9bc8c 100644 --- a/src/systems/points.rs +++ b/src/systems/points.rs @@ -9,10 +9,11 @@ fn update_point( point: &mut VerletPoint, acceleration: Vec3, friction: f32, + dt: f32, ) { let position = transform.translation; let velocity = point.old_position.map_or(Vec3::ZERO, |pos| position - pos); - transform.translation += velocity * friction + acceleration; + transform.translation += velocity * friction + (acceleration / point.mass) * friction * dt * dt; point.old_position = Some(position); } @@ -22,18 +23,30 @@ pub fn update_points( time: Res