Skip to main content

ParticleSystem

Struct ParticleSystem 

Source
pub struct ParticleSystem {
    pub particles: Vec<Particle>,
    pub emitters: Vec<ParticleEmitter>,
    pub gravity: Vec3Fix,
    pub damping: Fix128,
    pub max_particles: usize,
}
Expand description

A particle system managing particles and emitters.

Fields§

§particles: Vec<Particle>

All particles (alive and dead)

§emitters: Vec<ParticleEmitter>

Particle emitters

§gravity: Vec3Fix

Gravity vector

§damping: Fix128

Linear velocity damping (0 = full damping, 1 = no damping)

§max_particles: usize

Maximum number of particles allowed

Implementations§

Source§

impl ParticleSystem

Source

pub fn new(max_particles: usize, gravity: Vec3Fix) -> Self

Create a new particle system.

§Arguments
  • max_particles: Maximum number of live particles
  • gravity: Gravity vector applied to all particles
Source

pub fn add_emitter(&mut self, emitter: ParticleEmitter) -> usize

Add a particle emitter and return its index.

Source

pub fn alive_count(&self) -> usize

Count the number of alive particles.

Source

pub fn step(&mut self, dt: Fix128, rng: &mut DeterministicRng)

Step the particle system by dt seconds.

  1. Emit new particles from each emitter
  2. Apply gravity and damping to all alive particles
  3. Integrate positions (Euler)
  4. Age particles and kill expired ones

The rng parameter ensures deterministic emission patterns.

Source

pub fn step_with_landing( &mut self, dt: Fix128, rng: &mut DeterministicRng, targets: &[LandingTarget<'_>], max_travel: Fix128, respawn_emitter: usize, ) -> Vec<LandingEvent>

Step like step, but stop each particle at the first LandingTarget its path enters, report it and re-emit it from emitters[respawn_emitter] (rain hitting shapes and falling again).

The velocity update is the same as step (v += g·dt, v *= damping). The straight path x → x + v·dt is then sampled at n = ceil(|v·dt| / max_travel) evenly spaced points (k/n for k = 1..=n, the last one is exactly the position step would produce), and the targets are probed at each sample in order. The first occupied sample produces a LandingEvent; the slot is then refilled with a fresh particle from the respawn emitter (same position / velocity law as normal emission, drawing from rng), so landing never changes the number of live particles. Particles that land are not aged or expired in that step.

A target whose extent along the path is longer than max_travel cannot be skipped, whatever dt is: consecutive samples are at most max_travel apart. The cost is O(|v·dt| / max_travel) probes per particle per step. With no targets the result is bit-identical to step.

§Panics

If max_travel <= 0, if respawn_emitter is not a valid emitter index, or if a particle’s step would need more than 2^20 samples (|v·dt| / max_travel > 2^20).

§Example
use alice_physics::math::{Fix128, Vec3Fix};
use alice_physics::particle::{LandingTarget, ParticleEmitter, ParticleSystem};
use alice_physics::rng::DeterministicRng;
use alice_physics::sdf_collider::ClosureSdf;

// floor y = 0
let floor = ClosureSdf::new(|_, y, _| y, |_, _, _| (0.0, 1.0, 0.0));
let mut rain = ParticleSystem::new(64, Vec3Fix::from_int(0, -10, 0));
rain.damping = Fix128::ONE;
rain.add_emitter(ParticleEmitter::new(
    Vec3Fix::from_int(0, 5, 0),
    Vec3Fix::from_int(0, -1, 0),
    Fix128::from_ratio(1, 4),
    Fix128::from_int(120),
    Fix128::ONE,
    Fix128::from_int(100),
    Fix128::ONE,
));
let mut rng = DeterministicRng::new(7);
let targets = [LandingTarget::Field(&floor)];
let mut landed = 0;
for _ in 0..240 {
    let events = rain.step_with_landing(
        Fix128::from_ratio(1, 60),
        &mut rng,
        &targets,
        Fix128::from_ratio(1, 20),
        0,
    );
    for e in &events {
        assert!(e.normal.y > Fix128::ZERO);
    }
    landed += events.len();
}
assert!(landed > 0);
Source

pub fn apply_force_field(&mut self, field: &ForceField)

Apply a force field to all alive particles.

The force is crate::force::field_force_at (the same laws as crate::force::compute_force for rigid bodies), applied as a velocity impulse: v += F/m * dt. Uses a fixed dt of 1/60 for the impulse (since force fields are typically applied once per frame).

Trait Implementations§

Source§

impl Debug for ParticleSystem

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.