Skip to main content

euv_engine/particle/
struct.rs

1use super::*;
2
3/// A deterministic xorshift64* pseudo-random generator used by particle
4/// emitters.
5///
6/// The engine intentionally avoids both the `rand` crate (dependency weight
7/// for a wasm target) and `js_sys::Math::random` (non-deterministic, and
8/// unusable outside a JS runtime, which would make host-side unit tests
9/// impossible). A seeded generator keeps emitter behavior reproducible.
10#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
11pub struct ParticleRng {
12    /// The current generator state. Never zero after construction.
13    #[get(pub(crate), type(copy))]
14    #[get_mut(pub(crate))]
15    #[set(pub(crate))]
16    pub(crate) state: u64,
17}
18
19/// The static configuration of a particle emitter, describing how new
20/// particles are spawned and how they evolve over their lifetime.
21#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
22pub struct ParticleConfig {
23    /// The number of particles spawned per second while the emitter is active.
24    #[get(type(copy))]
25    #[set(pub(crate))]
26    pub(crate) emission_rate: f64,
27    /// The maximum number of simultaneously live particles.
28    #[get(type(copy))]
29    #[set(pub(crate))]
30    pub(crate) max_particles: usize,
31    /// The minimum particle lifetime in seconds.
32    #[get(type(copy))]
33    #[set(pub(crate))]
34    pub(crate) lifetime_min: f64,
35    /// The maximum particle lifetime in seconds.
36    #[get(type(copy))]
37    #[set(pub(crate))]
38    pub(crate) lifetime_max: f64,
39    /// The minimum initial particle speed in world units per second.
40    #[get(type(copy))]
41    #[set(pub(crate))]
42    pub(crate) speed_min: f64,
43    /// The maximum initial particle speed in world units per second.
44    #[get(type(copy))]
45    #[set(pub(crate))]
46    pub(crate) speed_max: f64,
47    /// The central emission direction in radians.
48    #[get(type(copy))]
49    #[new(skip)]
50    pub(crate) angle: f64,
51    /// The total cone width around `angle` in radians within which
52    /// particle directions are uniformly randomized.
53    #[get(type(copy))]
54    #[new(skip)]
55    pub(crate) spread: f64,
56    /// The constant acceleration applied to every live particle.
57    #[get(type(copy))]
58    #[new(skip)]
59    pub(crate) gravity: Vector2D,
60    /// The particle color at birth.
61    #[get(type(copy))]
62    #[new(skip)]
63    pub(crate) color_start: Color,
64    /// The particle color at death. Set its alpha to 0.0 for a fade-out.
65    #[get(type(copy))]
66    #[new(skip)]
67    pub(crate) color_end: Color,
68    /// The particle radius at birth in world units.
69    #[get(type(copy))]
70    #[new(skip)]
71    pub(crate) size_start: f64,
72    /// The particle radius at death in world units.
73    #[get(type(copy))]
74    #[new(skip)]
75    pub(crate) size_end: f64,
76}
77
78/// A single live particle.
79#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
80pub struct Particle {
81    /// The current world-space position.
82    #[get(pub(crate), type(copy))]
83    #[get_mut(pub(crate))]
84    #[set(pub(crate))]
85    pub(crate) position: Vector2D,
86    /// The current velocity in world units per second.
87    #[get(pub(crate), type(copy))]
88    #[get_mut(pub(crate))]
89    #[set(pub(crate))]
90    pub(crate) velocity: Vector2D,
91    /// The time this particle has been alive, in seconds.
92    #[get(pub(crate), type(copy))]
93    #[get_mut(pub(crate))]
94    #[set(pub(crate))]
95    pub(crate) age: f64,
96    /// The total lifetime of this particle, in seconds.
97    #[get(pub(crate), type(copy))]
98    #[set(pub(crate))]
99    pub(crate) lifetime: f64,
100}
101
102/// A point emitter that continuously (or in bursts) spawns particles,
103/// integrates their motion, and records them into a `DrawList` as colored
104/// circles whose color and size interpolate over each particle's lifetime.
105#[derive(Clone, Data, Debug, New, PartialEq)]
106pub struct ParticleEmitter {
107    /// The world-space emission point.
108    #[get(pub(crate), type(copy))]
109    #[get_mut(pub(crate))]
110    #[set(pub(crate))]
111    pub(crate) position: Vector2D,
112    /// The emitter configuration.
113    #[get(pub(crate), type(copy))]
114    #[set(pub(crate))]
115    pub(crate) config: ParticleConfig,
116    /// All currently live particles.
117    #[get(pub(crate), type(clone))]
118    #[get_mut(pub(crate))]
119    #[set(pub(crate))]
120    #[new(skip)]
121    pub(crate) particles: Vec<Particle>,
122    /// The fractional particle spawn budget carried between updates.
123    #[get(pub(crate), type(copy))]
124    #[get_mut(pub(crate))]
125    #[set(pub(crate))]
126    #[new(skip)]
127    pub(crate) emit_accumulator: f64,
128    /// Whether continuous emission is currently enabled.
129    #[get(type(copy))]
130    #[set(pub(crate))]
131    #[new(skip)]
132    pub(crate) active: bool,
133    /// The emitter's deterministic random generator.
134    #[get(pub(crate), type(copy))]
135    #[get_mut(pub(crate))]
136    #[set(pub(crate))]
137    #[new(skip)]
138    pub(crate) rng: ParticleRng,
139}