1use crate::vector3::Vector3D;
10use crate::particle_system_data3::*;
11use crate::collider3::Collider3Ptr;
12use crate::particle_emitter3::ParticleEmitter3Ptr;
13use crate::vector_field3::VectorField3Ptr;
14use crate::constant_vector_field3::ConstantVectorField3;
15use crate::animation::{Animation, Frame};
16use crate::physics_animation::{PhysicsAnimation, PhysicsAnimationData};
17use log::info;
18use std::time::SystemTime;
19use std::sync::{RwLock, Arc};
20use rayon::prelude::*;
21
22pub struct ParticleSystemSolver3 {
35 pub _drag_coefficient: f64,
36 pub _restitution_coefficient: f64,
37 pub _gravity: Vector3D,
38
39 pub _particle_system_data: ParticleSystemData3Ptr,
40 pub _new_positions: Vec<Vector3D>,
41 pub _new_velocities: Vec<Vector3D>,
42 pub _collider: Option<Collider3Ptr>,
43 pub _emitter: Option<ParticleEmitter3Ptr>,
44 pub _wind: VectorField3Ptr,
45
46 pub _animation_data: PhysicsAnimationData,
47}
48
49impl ParticleSystemSolver3 {
50 pub fn new_default() -> ParticleSystemSolver3 {
52 return ParticleSystemSolver3::new(1.0e-3, 1.0e-3);
53 }
54
55 pub fn new(radius: f64,
57 mass: f64) -> ParticleSystemSolver3 {
58 let data = Arc::new(RwLock::new(ParticleSystemData3::new_default()));
59 data.write().unwrap().set_radius(radius);
60 data.write().unwrap().set_mass(mass);
61 let wind = Arc::new(RwLock::new(ConstantVectorField3::new(Some(Vector3D::new_default()))));
62 return ParticleSystemSolver3 {
63 _drag_coefficient: 1.0e-4,
64 _restitution_coefficient: 0.0,
65 _gravity: Vector3D::new(0.0, crate::constants::K_GRAVITY, 0.0),
66 _particle_system_data: data,
67 _new_positions: vec![],
68 _new_velocities: vec![],
69 _collider: None,
70 _emitter: None,
71 _wind: wind,
72 _animation_data: PhysicsAnimationData::new(),
73 };
74 }
75
76 pub fn builder() -> Builder {
78 return Builder::new();
79 }
80}
81
82impl ParticleSystemSolver3 {
83 pub fn drag_coefficient(&self) -> f64 {
85 return self._drag_coefficient;
86 }
87
88 pub fn set_drag_coefficient(&mut self, new_drag_coefficient: f64) {
97 self._drag_coefficient = f64::max(new_drag_coefficient, 0.0);
98 }
99
100 pub fn restitution_coefficient(&self) -> f64 {
102 return self._restitution_coefficient;
103 }
104
105 pub fn set_restitution_coefficient(&mut self, new_restitution_coefficient: f64) {
115 self._restitution_coefficient = crate::math_utils::clamp(new_restitution_coefficient, 0.0, 1.0);
116 }
117
118 pub fn gravity(&self) -> &Vector3D {
120 return &self._gravity;
121 }
122
123 pub fn set_gravity(&mut self, new_gravity: &Vector3D) {
125 self._gravity = *new_gravity;
126 }
127
128 pub fn particle_system_data(&self) -> &ParticleSystemData3Ptr {
137 return &self._particle_system_data;
138 }
139
140 pub fn collider(&self) -> &Option<Collider3Ptr> {
142 return &self._collider;
143 }
144
145 pub fn set_collider(&mut self, new_collider: &Collider3Ptr) {
147 self._collider = Some(new_collider.clone());
148 }
149
150 pub fn emitter(&self) -> &Option<ParticleEmitter3Ptr> {
152 return &self._emitter;
153 }
154
155 pub fn set_emitter(&mut self, new_emitter: &ParticleEmitter3Ptr) {
157 self._emitter = Some(new_emitter.clone());
158 new_emitter.write().unwrap().set_target(self._particle_system_data.clone());
159 }
160
161 pub fn wind(&self) -> &VectorField3Ptr {
163 return &self._wind;
164 }
165
166 pub fn set_wind(&mut self, new_wind: &VectorField3Ptr) {
175 self._wind = new_wind.clone();
176 }
177}
178
179impl Animation for ParticleSystemSolver3 {
180 fn on_update(&mut self, frame: &Frame) {
181 PhysicsAnimation::on_update(self, frame);
182 }
183}
184
185impl PhysicsAnimation for ParticleSystemSolver3 {
186 fn on_advance_time_step(&mut self, time_step_in_seconds: f64) {
187 self.begin_advance_time_step(time_step_in_seconds);
188
189 let mut timer = SystemTime::now();
190 self.accumulate_forces(time_step_in_seconds);
191 info!("Accumulating forces took {} seconds", timer.elapsed().unwrap().as_secs_f64());
192
193 timer = SystemTime::now();
194 self.time_integration(time_step_in_seconds);
195 info!("Time integration took {} seconds", timer.elapsed().unwrap().as_secs_f64());
196
197 timer = SystemTime::now();
198 self.resolve_collision();
199 info!("Resolving collision took {} seconds", timer.elapsed().unwrap().as_secs_f64());
200
201 self.end_advance_time_step(time_step_in_seconds);
202 }
203
204 fn on_initialize(&mut self) {
205 let mut timer = SystemTime::now();
208 self.update_collider(0.0);
209 info!("Update collider took {} seconds", timer.elapsed().unwrap().as_secs_f64());
210
211 timer = SystemTime::now();
212 self.update_emitter(0.0);
213 info!("Update emitter took {} seconds", timer.elapsed().unwrap().as_secs_f64());
214 }
215
216 fn view(&self) -> &PhysicsAnimationData {
217 return &self._animation_data;
218 }
219
220 fn view_mut(&mut self) -> &mut PhysicsAnimationData {
221 return &mut self._animation_data;
222 }
223}
224
225impl ParticleSystemSolver3 {
226 fn accumulate_forces(&mut self, _: f64) {
228 self.accumulate_external_forces();
230 }
231
232 fn on_begin_advance_time_step(&mut self, _: f64) {}
234
235 fn on_end_advance_time_step(&mut self, _: f64) {}
237
238 fn resolve_collision(&mut self) {
240 if let Some(collider) = &self._collider {
241 let radius = self._particle_system_data.read().unwrap().radius();
242 let restitution = self._restitution_coefficient;
243 (&mut self._new_positions, &mut self._new_velocities).into_par_iter().for_each(|(pos, vel)| {
244 collider.read().unwrap().resolve_collision(
245 radius,
246 restitution,
247 pos,
248 vel);
249 });
250 }
251 }
252
253 fn begin_advance_time_step(&mut self, time_step_in_seconds: f64) {
254 self._particle_system_data.write().unwrap().forces_mut().fill(Vector3D::new_default());
256
257 let mut timer = SystemTime::now();
259 self.update_collider(time_step_in_seconds);
260 info!("Update collider took {} seconds", timer.elapsed().unwrap().as_secs_f64());
261
262 timer = SystemTime::now();
263 self.update_emitter(time_step_in_seconds);
264 info!("Update emitter took {} seconds", timer.elapsed().unwrap().as_secs_f64());
265
266 let n = self._particle_system_data.read().unwrap().number_of_particles();
268 self._new_positions.resize(n, Vector3D::new_default());
269 self._new_velocities.resize(n, Vector3D::new_default());
270
271 self.on_begin_advance_time_step(time_step_in_seconds);
272 }
273
274 fn end_advance_time_step(&mut self, time_step_in_seconds: f64) {
275 (self._particle_system_data.write().unwrap().positions_mut(),
277 self._particle_system_data.write().unwrap().velocities_mut(),
278 &self._new_positions, &self._new_velocities).into_par_iter().for_each(|(pos, vel, pos_new, vel_new)| {
279 *pos = *pos_new;
280 *vel = *vel_new;
281 });
282
283 self.on_end_advance_time_step(time_step_in_seconds);
284 }
285
286 fn accumulate_external_forces(&mut self) {
287 let mass = self._particle_system_data.read().unwrap().mass();
288
289 (self._particle_system_data.write().unwrap().forces_mut(),
290 self._particle_system_data.read().unwrap().positions(),
291 self._particle_system_data.read().unwrap().velocities()).into_par_iter().for_each(|(force, pos, vel)| {
292 let mut f = self._gravity * mass;
294
295 let relative_vel = *vel - self._wind.read().unwrap().sample(pos);
297 f += relative_vel * -self._drag_coefficient;
298
299 *force += f;
300 });
301 }
302
303 fn time_integration(&mut self, time_step_in_seconds: f64) {
304 let mass = self._particle_system_data.read().unwrap().mass();
305
306 (&mut self._new_positions, &mut self._new_velocities,
307 self._particle_system_data.read().unwrap().forces(),
308 self._particle_system_data.read().unwrap().positions(),
309 self._particle_system_data.read().unwrap().velocities()).into_par_iter().for_each(|(new_pos, new_vel, force, pos, vel)| {
310 *new_vel = *vel + *force * time_step_in_seconds / mass;
312
313 *new_pos = *pos + *new_vel * time_step_in_seconds;
315 });
316 }
317
318 fn update_collider(&mut self, time_step_in_seconds: f64) {
319 if let Some(collider) = &self._collider {
320 collider.write().unwrap().update(self.current_time_in_seconds(),
321 time_step_in_seconds);
322 }
323 }
324
325 fn update_emitter(&mut self, time_step_in_seconds: f64) {
326 if let Some(emitter) = &self._emitter {
327 emitter.write().unwrap().update(self.current_time_in_seconds(),
328 time_step_in_seconds);
329 }
330 }
331}
332
333pub type ParticleSystemSolver3Ptr = Arc<RwLock<ParticleSystemSolver3>>;
335
336pub trait ParticleSystemSolverBuilderBase3 {
341 fn with_radius(&mut self, radius: f64) -> &mut Self;
343
344 fn with_mass(&mut self, mass: f64) -> &mut Self;
346}
347
348pub struct Builder {
352 _radius: f64,
353 _mass: f64,
354}
355
356impl Builder {
357 pub fn build(&mut self) -> ParticleSystemSolver3 {
359 return ParticleSystemSolver3::new(self._radius, self._mass);
360 }
361
362 pub fn make_shared(&mut self) -> ParticleSystemSolver3Ptr {
364 return ParticleSystemSolver3Ptr::new(RwLock::new(self.build()));
365 }
366
367 pub fn new() -> Builder {
369 return Builder {
370 _radius: 1.0e-3,
371 _mass: 1.0e-3,
372 };
373 }
374}
375
376impl ParticleSystemSolverBuilderBase3 for Builder {
377 fn with_radius(&mut self, radius: f64) -> &mut Self {
378 self._radius = radius;
379 return self;
380 }
381
382 fn with_mass(&mut self, mass: f64) -> &mut Self {
383 self._mass = mass;
384 return self;
385 }
386}