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