rapier3d/dynamics/soft_body/
soft_body_motion.rs1use super::SoftBody;
3use crate::dynamics::{RigidBodyHandle, RigidBodySet};
4use crate::math::{Real, Vector};
5#[cfg(not(feature = "std"))]
6#[allow(unused_imports)]
7use simba::scalar::{ComplexField as _, RealField as _};
8
9#[derive(Copy, Clone, Debug)]
12#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
13pub struct SoftParticleAttachment {
14 pub particle: u32,
16 pub body: RigidBodyHandle,
18 pub local_anchor: Vector,
20 pub(crate) impulse: Vector,
22}
23
24impl SoftParticleAttachment {
25 pub fn impulse(&self) -> Vector {
28 self.impulse
29 }
30}
31
32impl SoftBody {
33 pub fn set_particle_pinned(&mut self, i: usize, pinned: bool) {
37 let particle = &mut self.particles[i];
38 if pinned {
39 particle.inv_mass = 0.0;
41 particle.velocity = Vector::ZERO;
42 particle.next_position = Some(particle.position);
43 } else {
44 particle.inv_mass = crate::utils::inv(particle.mass);
45 particle.next_position = None;
46 }
47 self.modified = true;
48 }
49
50 pub fn attach_particle(&mut self, i: usize, body: RigidBodyHandle, bodies: &RigidBodySet) {
54 let particle = &self.particles[i];
55 let local_anchor = bodies
56 .get(body)
57 .map(|rb| rb.position().inverse_transform_point(particle.position))
58 .unwrap_or(particle.position);
59 self.attachments.push(SoftParticleAttachment {
60 particle: i as u32,
61 body,
62 local_anchor,
63 impulse: Vector::ZERO,
64 });
65 self.attachments_modified = true;
66 self.modified = true;
67 }
68
69 pub fn detach_particle(&mut self, i: usize) -> bool {
72 let before = self.attachments.len();
73 self.attachments.retain(|a| a.particle != i as u32);
74 let detached = self.attachments.len() != before;
75 if detached {
76 self.attachments_modified = true;
77 self.modified = true;
78 }
79 detached
80 }
81
82 pub fn particle_attachments(&self) -> &[SoftParticleAttachment] {
84 &self.attachments
85 }
86
87 pub fn add_force(&mut self, force: Vector, wake_up: bool) {
90 for p in &mut self.particles {
91 p.force += force;
92 }
93 if wake_up {
94 self.wake_up();
95 }
96 }
97
98 pub fn add_particle_force(&mut self, i: usize, force: Vector, wake_up: bool) {
101 self.particles[i].force += force;
102 if wake_up {
103 self.wake_up();
104 }
105 }
106
107 pub fn reset_forces(&mut self, wake_up: bool) {
109 for p in &mut self.particles {
110 p.force = Vector::ZERO;
111 }
112 if wake_up {
113 self.wake_up();
114 }
115 }
116
117 pub fn apply_impulse(&mut self, impulse: Vector, wake_up: bool) {
120 for p in &mut self.particles {
121 if p.inv_mass > 0.0 {
122 p.velocity += impulse;
123 }
124 }
125 if wake_up {
126 self.wake_up();
127 }
128 }
129
130 pub fn apply_particle_impulse(&mut self, i: usize, impulse: Vector, wake_up: bool) {
133 let p = &mut self.particles[i];
134 p.velocity += impulse * p.inv_mass;
135 if wake_up {
136 self.wake_up();
137 }
138 }
139
140 pub fn apply_impulse_at_point(
144 &mut self,
145 impulse: Vector,
146 point: Vector,
147 falloff_radius: Real,
148 wake_up: bool,
149 ) {
150 for p in &mut self.particles {
151 if p.inv_mass == 0.0 {
152 continue;
153 }
154 let scale = if falloff_radius > 0.0 {
155 1.0 - (p.position - point).length() / falloff_radius
156 } else {
157 1.0
158 };
159 if scale > 0.0 {
160 p.velocity += impulse * (scale * p.inv_mass);
161 }
162 }
163 if wake_up {
164 self.wake_up();
165 }
166 }
167
168 pub fn apply_radial_impulse(
172 &mut self,
173 center: Vector,
174 magnitude: Real,
175 falloff_radius: Real,
176 wake_up: bool,
177 ) {
178 for p in &mut self.particles {
179 if p.inv_mass == 0.0 {
180 continue;
181 }
182 let offset = p.position - center;
183 let distance = offset.length();
184 if distance <= Real::EPSILON {
185 continue;
186 }
187 let scale = if falloff_radius > 0.0 {
188 1.0 - distance / falloff_radius
189 } else {
190 1.0
191 };
192 if scale > 0.0 {
193 p.velocity += offset * (magnitude * scale * p.inv_mass / distance);
194 }
195 }
196 if wake_up {
197 self.wake_up();
198 }
199 }
200}