1use std::f32::consts::PI;
4
5use crate::component::Component;
6use crate::math::*;
7use crate::renderer::Quad;
8use crate::transform::Transform2D;
9
10const MAX_PARTICLES: usize = 100000;
12
13#[derive(Debug, Clone)]
17pub struct ParticleProps {
18 pub lifetime: f32,
20 pub velocity: Vec2,
22 pub velocity_modifier: Vec2,
24 pub color_start: Color32,
26 pub color_end: Color32,
28 pub color_modifier: Color32,
30 pub burst_count: u32,
32 pub size: Vec2,
34}
35
36impl Default for ParticleProps {
37 fn default() -> Self {
38 Self {
39 lifetime: 10.0,
40 velocity: Vec2::new(0.0, -0.3),
41 velocity_modifier: Vec2::new(0.25, 0.2),
42 color_start: Color32(0.0, 0.6, 1.0, 0.9),
43 color_end: Color32(0.95, 0.2, 1.0, 1.0),
44 color_modifier: Color32(0.2, 0.4, 0.1, 0.0),
45 burst_count: 15,
46 size: Vec2::new(0.05, 0.05),
47 }
48 }
49}
50
51impl ParticleProps {
52 pub const fn fire() -> Self {
54 Self {
55 lifetime: 10.0,
56 velocity: Vec2::new(0.0, -0.2),
57 velocity_modifier: Vec2::new(0.15, 0.1),
58 color_start: Color32(1.0, 1.0, 0.0, 1.0),
59 color_end: Color32(1.0, 0.0, 0.0, 1.0),
60 color_modifier: Color32(0.2, 0.2, 0.3, 0.0),
61 burst_count: 5,
62 size: Vec2::new(0.05, 0.05),
63 }
64 }
65
66 pub const fn smoke() -> Self {
68 Self {
69 lifetime: 15.0,
70 velocity: Vec2::new(0.0, -0.4),
71 velocity_modifier: Vec2::new(0.3, 0.2),
72 color_start: Color32(0.7, 0.7, 0.7, 1.0),
73 color_end: Color32::BLACK,
74 color_modifier: Color32(0.4, 0.4, 0.4, 0.0),
75 burst_count: 20,
76 size: Vec2::new(0.1, 0.15),
77 }
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct Particle {
84 transform: Transform2D,
85 lifetime: f32,
86 velocity: Vec2,
87 color: Color32,
88 color_start: Color32,
89 color_end: Color32,
90 age: f32,
91 alive: bool,
92}
93
94impl Default for Particle {
95 fn default() -> Self {
96 Self {
97 transform: Transform2D::new_with_scale(0.1, 0.1),
98 lifetime: 10.0,
99 velocity: Vec2::new(0.0, 0.0),
100 color: Color32::ZEROES,
101 color_start: Color32::WHITE,
102 color_end: Color32::WHITE,
103 age: 0.0,
104 alive: false,
105 }
106 }
107}
108
109impl Component for Particle {
110 fn init(&mut self) {
111 self.transform.rotation = f32::random_range_max(PI);
112 self.color = self.color_start;
113 self.alive = true;
114 self.age = 0.0;
115 }
116
117 fn update(&mut self, delta_time: f32) {
118 self.age += delta_time;
119 if self.age > self.lifetime {
120 self.alive = false;
121 } else {
122 self.transform.position += self.velocity * delta_time;
123 self.transform.rotation += f32::random_range(-1.0, 1.0) * delta_time;
124 self.color = Color32::lerp(self.color_start, self.color_end, self.age / self.lifetime);
125 }
126 }
127
128 fn as_any(&self) -> &dyn std::any::Any {
129 self
130 }
131
132 fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
133 self
134 }
135}
136
137impl From<&ParticleProps> for Particle {
138 fn from(properties: &ParticleProps) -> Self {
139 Self {
140 transform: Transform2D::new_with_scale(properties.size.x, properties.size.y),
141 lifetime: properties.lifetime,
142 velocity: {
143 properties.velocity
144 + Vec2::random_range(
145 -properties.velocity_modifier,
146 properties.velocity_modifier,
147 )
148 },
149 color_start: properties.color_start
150 + Color32::random_range(properties.color_modifier, properties.color_modifier),
151 color_end: properties.color_end
152 + Color32::random_range(properties.color_modifier, properties.color_modifier),
153 ..Default::default()
154 }
155 }
156}
157
158#[derive(Debug, Clone)]
160pub struct ParticleSystem {
161 pub transform: Transform2D,
163 emission: ParticleProps,
164 particles: Vec<Particle>,
165 index: usize,
166 pub alive: bool,
168}
169
170impl Default for ParticleSystem {
171 fn default() -> Self {
172 Self {
173 emission: ParticleProps::default(),
174 particles: Vec::with_capacity(MAX_PARTICLES),
175 index: 0,
176 transform: Transform2D::default(),
177 alive: false,
178 }
179 }
180}
181
182impl Component for ParticleSystem {
183 fn init(&mut self) {
184 self.particles.fill_with(Particle::default);
185 self.alive = true;
186 }
187
188 fn update(&mut self, delta_time: f32) {
189 if !self.alive {
191 return;
192 }
193
194 self.emit_many(self.emission.burst_count);
195 for particle in self.particles.iter_mut() {
196 if particle.alive {
197 particle.update(delta_time);
198 }
199 }
200 }
201 fn get_quads(&self) -> Option<Vec<Quad>> {
203 Some(
204 self.particles
205 .iter()
206 .filter(|particle| particle.alive)
207 .map(|particle| {
208 Quad::new_from_position_and_rotation_and_size_and_color(
209 particle.transform.position.x,
210 particle.transform.position.y,
211 particle.transform.rotation,
212 particle.transform.scale.x,
213 particle.transform.scale.y,
214 particle.color,
215 )
216 })
217 .collect(),
218 )
219 }
220
221 fn as_any(&self) -> &dyn std::any::Any {
222 self
223 }
224
225 fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
226 self
227 }
228}
229
230impl ParticleSystem {
231 pub fn new_from_emission(emission: ParticleProps) -> Self {
233 Self {
234 emission,
235 ..Default::default()
236 }
237 }
238
239 pub fn new_from_emission_and_position(emission: ParticleProps, pos_x: f32, pos_y: f32) -> Self {
241 Self {
242 emission,
243 transform: Transform2D::new_with_position(pos_x, pos_y),
244 ..Default::default()
245 }
246 }
247
248 pub fn toggle_alive(&mut self) {
250 self.alive = !self.alive;
251 }
252
253 pub fn emit(&mut self) {
255 if self.index >= MAX_PARTICLES {
256 self.index = 0;
257 }
258
259 let mut new_particle = Particle::from(&self.emission);
260 new_particle.transform = self.transform + new_particle.transform;
261
262 let particle = self.particles.get_mut(self.index);
263
264 if let Some(particle) = particle {
265 particle.transform = new_particle.transform;
266 particle.lifetime = new_particle.lifetime;
267 particle.velocity = new_particle.velocity;
268 particle.color_start = new_particle.color_start;
269 particle.color_end = new_particle.color_end;
270
271 particle.init();
272 } else {
273 new_particle.init();
274 self.particles.push(new_particle);
275 }
276 self.index += 1;
277 }
278
279 pub fn emit_many(&mut self, count: u32) {
281 for _ in 0..count {
282 self.emit()
283 }
284 }
285}