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(type(copy))]
14    #[get_mut(pub(crate))]
15    #[set(pub(crate))]
16    pub 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///
22/// Every field is publicly settable, so a caller composes a config by
23/// starting from [`ParticleConfig::default`] and overriding the tunables it
24/// cares about before handing it to [`ParticleEmitter::create`].
25#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
26pub struct ParticleConfig {
27    /// The number of particles spawned per second while the emitter is active.
28    #[get(pub(crate), type(copy))]
29    #[get_mut(pub(crate))]
30    pub emission_rate: f64,
31    /// The maximum number of simultaneously live particles.
32    #[get(pub(crate), type(copy))]
33    pub max_particles: usize,
34    /// The minimum particle lifetime in seconds.
35    #[get(type(copy))]
36    pub lifetime_min: f64,
37    /// The maximum particle lifetime in seconds.
38    #[get(type(copy))]
39    #[get_mut(pub(crate))]
40    pub lifetime_max: f64,
41    /// The minimum initial particle speed in world units per second.
42    #[get(pub(crate), type(copy))]
43    #[get_mut(pub(crate))]
44    pub speed_min: f64,
45    /// The maximum initial particle speed in world units per second.
46    #[get(pub(crate), type(copy))]
47    #[get_mut(pub(crate))]
48    pub speed_max: f64,
49    /// The central emission direction in radians.
50    #[get(pub(crate), type(copy))]
51    #[new(skip)]
52    #[set(pub(crate))]
53    #[get_mut(pub(crate))]
54    pub(crate) angle: f64,
55    /// The total cone width around `angle` in radians within which
56    /// particle directions are uniformly randomized.
57    #[get(pub(crate), type(copy))]
58    #[new(skip)]
59    #[set(pub(crate))]
60    #[get_mut(pub(crate))]
61    pub(crate) spread: f64,
62    /// The constant acceleration applied to every live particle.
63    #[get(pub(crate), type(copy))]
64    #[new(skip)]
65    #[get_mut(pub(crate))]
66    pub gravity: Vector2D,
67    /// The particle color at birth.
68    #[get(pub(crate), type(copy))]
69    #[new(skip)]
70    #[set(pub(crate))]
71    #[get_mut(pub(crate))]
72    pub(crate) color_start: Color,
73    /// The particle color at death. Set its alpha to 0.0 for a fade-out.
74    #[get(pub(crate), type(copy))]
75    #[new(skip)]
76    #[set(pub(crate))]
77    #[get_mut(pub(crate))]
78    pub(crate) color_end: Color,
79    /// The particle radius at birth in world units.
80    #[get(pub(crate), type(copy))]
81    #[new(skip)]
82    #[get_mut(pub(crate))]
83    pub size_start: f64,
84    /// The particle radius at death in world units.
85    #[get(pub(crate), type(copy))]
86    #[new(skip)]
87    #[get_mut(pub(crate))]
88    pub size_end: f64,
89}
90
91/// A single live particle.
92#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
93pub struct Particle {
94    /// The current world-space position.
95    #[get(type(copy))]
96    #[get_mut(pub(crate))]
97    pub position: Vector2D,
98    #[get(type(clone))]
99    #[get_mut(pub(crate))]
100    pub velocity: Vector2D,
101    /// The time this particle has been alive, in seconds.
102    #[get(pub(crate), type(copy))]
103    #[get_mut(pub(crate))]
104    #[set(pub(crate))]
105    pub(crate) age: f64,
106    /// The total lifetime of this particle, in seconds.
107    #[get(type(copy))]
108    #[set(pub(crate))]
109    #[get_mut(pub(crate))]
110    pub lifetime: f64,
111}
112
113/// A point emitter that continuously (or in bursts) spawns particles,
114/// integrates their motion, and records them into a `DrawList` as colored
115/// circles whose color and size interpolate over each particle's lifetime.
116#[derive(Clone, Data, Debug, New, PartialEq)]
117pub struct ParticleEmitter {
118    /// The world-space emission point.
119    #[get(type(copy))]
120    #[get_mut(pub(crate))]
121    pub position: Vector2D,
122    /// The emitter configuration.
123    #[get(type(clone))]
124    #[get_mut(pub(crate))]
125    pub config: ParticleConfig,
126    /// All currently live particles.
127    #[get(type(clone))]
128    #[get_mut(pub(crate))]
129    #[set(pub(crate))]
130    #[new(skip)]
131    pub particles: Vec<Particle>,
132    /// The fractional particle spawn budget carried between updates.
133    #[get(type(copy))]
134    #[get_mut(pub(crate))]
135    #[set(pub(crate))]
136    #[new(skip)]
137    pub emit_accumulator: f64,
138    /// Whether continuous emission is currently enabled.
139    #[get(pub(crate), type(copy))]
140    #[new(skip)]
141    #[get_mut(pub(crate))]
142    pub active: bool,
143    /// The emitter's deterministic random generator.
144    #[get(type(copy), pub(crate))]
145    #[get_mut(pub(crate))]
146    #[set(pub(crate))]
147    #[new(skip)]
148    pub(crate) rng: ParticleRng,
149}