1use nalgebra_glm::{Vec3, Vec4};
4
5#[derive(Debug, Clone)]
6pub struct ParticleTextureUpload {
7 pub slot: u32,
8 pub rgba_data: Vec<u8>,
9 pub width: u32,
10 pub height: u32,
11}
12
13#[derive(
15 Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize, enum2schema::Schema,
16)]
17#[schema(string_enum)]
18pub enum EmitterType {
19 Firework,
21 Fire,
23 Smoke,
25 Sparks,
27 Trail,
29}
30
31#[derive(
33 Debug,
34 Clone,
35 Copy,
36 PartialEq,
37 Default,
38 serde::Serialize,
39 serde::Deserialize,
40 enum2schema::Schema,
41)]
42pub enum EmitterShape {
43 #[default]
45 Point,
46 Sphere { radius: f32 },
48 Cone { angle: f32, height: f32 },
50 Box { half_extents: Vec3 },
52}
53
54#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, enum2schema::Schema)]
59pub struct ColorGradient {
60 pub colors: Vec<(f32, Vec4)>,
62}
63
64impl Default for ColorGradient {
65 fn default() -> Self {
66 Self {
67 colors: vec![
68 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
69 (1.0, Vec4::new(1.0, 1.0, 1.0, 0.0)),
70 ],
71 }
72 }
73}
74
75impl ColorGradient {
76 pub fn firework_trail() -> Self {
78 Self {
79 colors: vec![
80 (0.0, Vec4::new(1.0, 1.0, 0.95, 1.0)),
81 (0.15, Vec4::new(1.0, 0.95, 0.7, 1.0)),
82 (0.4, Vec4::new(1.0, 0.7, 0.3, 0.85)),
83 (0.7, Vec4::new(1.0, 0.4, 0.1, 0.5)),
84 (1.0, Vec4::new(0.6, 0.15, 0.0, 0.0)),
85 ],
86 }
87 }
88
89 pub fn firework_explosion(base_color: Vec3) -> Self {
91 Self {
92 colors: vec![
93 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
94 (
95 0.05,
96 Vec4::new(
97 (base_color.x + 1.0) * 0.5,
98 (base_color.y + 1.0) * 0.5,
99 (base_color.z + 1.0) * 0.5,
100 1.0,
101 ),
102 ),
103 (
104 0.15,
105 Vec4::new(base_color.x, base_color.y, base_color.z, 1.0),
106 ),
107 (
108 0.4,
109 Vec4::new(base_color.x, base_color.y, base_color.z, 0.95),
110 ),
111 (
112 0.65,
113 Vec4::new(
114 base_color.x * 0.85,
115 base_color.y * 0.85,
116 base_color.z * 0.85,
117 0.7,
118 ),
119 ),
120 (
121 0.85,
122 Vec4::new(
123 base_color.x * 0.5,
124 base_color.y * 0.5,
125 base_color.z * 0.5,
126 0.3,
127 ),
128 ),
129 (1.0, Vec4::new(0.1, 0.1, 0.1, 0.0)),
130 ],
131 }
132 }
133
134 pub fn firework_willow(base_color: Vec3) -> Self {
136 Self {
137 colors: vec![
138 (0.0, Vec4::new(1.0, 1.0, 0.95, 1.0)),
139 (
140 0.1,
141 Vec4::new(base_color.x, base_color.y, base_color.z, 1.0),
142 ),
143 (
144 0.3,
145 Vec4::new(base_color.x, base_color.y, base_color.z, 0.9),
146 ),
147 (
148 0.6,
149 Vec4::new(
150 base_color.x * 0.7,
151 base_color.y * 0.8,
152 base_color.z * 0.6,
153 0.6,
154 ),
155 ),
156 (
157 0.85,
158 Vec4::new(
159 base_color.x * 0.4,
160 base_color.y * 0.5,
161 base_color.z * 0.3,
162 0.25,
163 ),
164 ),
165 (1.0, Vec4::new(0.1, 0.1, 0.05, 0.0)),
166 ],
167 }
168 }
169
170 pub fn firework_glitter() -> Self {
171 Self {
172 colors: vec![
173 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
174 (0.2, Vec4::new(1.0, 1.0, 0.9, 1.0)),
175 (0.5, Vec4::new(1.0, 0.95, 0.7, 0.9)),
176 (0.8, Vec4::new(1.0, 0.85, 0.5, 0.5)),
177 (1.0, Vec4::new(0.8, 0.6, 0.2, 0.0)),
178 ],
179 }
180 }
181
182 pub fn firework_crackle() -> Self {
183 Self {
184 colors: vec![
185 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
186 (0.1, Vec4::new(1.0, 0.95, 0.8, 1.0)),
187 (0.3, Vec4::new(1.0, 0.8, 0.4, 0.9)),
188 (0.6, Vec4::new(1.0, 0.5, 0.1, 0.6)),
189 (1.0, Vec4::new(0.4, 0.1, 0.0, 0.0)),
190 ],
191 }
192 }
193
194 pub fn flash_burst() -> Self {
195 Self {
196 colors: vec![
197 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
198 (0.15, Vec4::new(1.0, 1.0, 0.95, 1.0)),
199 (0.4, Vec4::new(1.0, 0.95, 0.8, 0.7)),
200 (0.7, Vec4::new(1.0, 0.9, 0.6, 0.3)),
201 (1.0, Vec4::new(1.0, 0.8, 0.4, 0.0)),
202 ],
203 }
204 }
205
206 pub fn comet_trail() -> Self {
207 Self {
208 colors: vec![
209 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
210 (0.1, Vec4::new(1.0, 0.98, 0.9, 1.0)),
211 (0.25, Vec4::new(1.0, 0.9, 0.6, 0.95)),
212 (0.5, Vec4::new(1.0, 0.7, 0.3, 0.8)),
213 (0.75, Vec4::new(1.0, 0.4, 0.1, 0.5)),
214 (1.0, Vec4::new(0.5, 0.1, 0.0, 0.0)),
215 ],
216 }
217 }
218
219 pub fn palm_frond(base_color: Vec3) -> Self {
220 Self {
221 colors: vec![
222 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
223 (
224 0.08,
225 Vec4::new(base_color.x, base_color.y, base_color.z, 1.0),
226 ),
227 (
228 0.25,
229 Vec4::new(base_color.x, base_color.y, base_color.z, 0.95),
230 ),
231 (
232 0.5,
233 Vec4::new(
234 base_color.x * 0.9,
235 base_color.y * 0.9,
236 base_color.z * 0.9,
237 0.85,
238 ),
239 ),
240 (
241 0.75,
242 Vec4::new(
243 base_color.x * 0.7,
244 base_color.y * 0.7,
245 base_color.z * 0.7,
246 0.5,
247 ),
248 ),
249 (
250 1.0,
251 Vec4::new(
252 base_color.x * 0.3,
253 base_color.y * 0.3,
254 base_color.z * 0.3,
255 0.0,
256 ),
257 ),
258 ],
259 }
260 }
261
262 pub fn crossette() -> Self {
263 Self {
264 colors: vec![
265 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
266 (0.1, Vec4::new(1.0, 0.95, 0.85, 1.0)),
267 (0.3, Vec4::new(1.0, 0.85, 0.6, 0.9)),
268 (0.6, Vec4::new(1.0, 0.6, 0.2, 0.7)),
269 (1.0, Vec4::new(0.6, 0.2, 0.0, 0.0)),
270 ],
271 }
272 }
273
274 pub fn strobe() -> Self {
275 Self {
276 colors: vec![
277 (0.0, Vec4::new(1.0, 1.0, 1.0, 1.0)),
278 (0.5, Vec4::new(1.0, 1.0, 1.0, 1.0)),
279 (1.0, Vec4::new(0.8, 0.8, 0.8, 0.0)),
280 ],
281 }
282 }
283
284 pub fn fire() -> Self {
285 Self {
286 colors: vec![
287 (0.0, Vec4::new(1.0, 1.0, 0.9, 1.0)),
288 (0.2, Vec4::new(1.0, 0.9, 0.4, 1.0)),
289 (0.4, Vec4::new(1.0, 0.5, 0.1, 0.9)),
290 (0.7, Vec4::new(0.9, 0.2, 0.0, 0.6)),
291 (1.0, Vec4::new(0.3, 0.05, 0.0, 0.0)),
292 ],
293 }
294 }
295
296 pub fn smoke() -> Self {
297 Self {
298 colors: vec![
299 (0.0, Vec4::new(0.4, 0.4, 0.45, 0.0)),
300 (0.1, Vec4::new(0.5, 0.5, 0.55, 0.6)),
301 (0.4, Vec4::new(0.55, 0.55, 0.6, 0.5)),
302 (0.7, Vec4::new(0.6, 0.6, 0.65, 0.3)),
303 (1.0, Vec4::new(0.65, 0.65, 0.7, 0.0)),
304 ],
305 }
306 }
307
308 pub fn sparks() -> Self {
309 Self {
310 colors: vec![
311 (0.0, Vec4::new(1.0, 1.0, 0.8, 1.0)),
312 (0.3, Vec4::new(1.0, 0.8, 0.2, 1.0)),
313 (0.7, Vec4::new(1.0, 0.4, 0.0, 0.7)),
314 (1.0, Vec4::new(0.5, 0.1, 0.0, 0.0)),
315 ],
316 }
317 }
318
319 pub fn to_gpu_data(&self) -> [[f32; 8]; 8] {
321 let mut data = [[0.0f32; 8]; 8];
322 for (index, (time, color)) in self.colors.iter().take(8).enumerate() {
323 data[index] = [*time, color.x, color.y, color.z, color.w, 0.0, 0.0, 0.0];
324 }
325 data
326 }
327
328 pub fn color_count(&self) -> u32 {
330 self.colors.len().min(8) as u32
331 }
332}
333
334#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, enum2schema::Schema)]
339pub struct ParticleEmitter {
340 pub emitter_type: EmitterType,
342 pub shape: EmitterShape,
344 pub position: Vec3,
346 pub direction: Vec3,
348 pub spawn_rate: f32,
350 pub burst_count: u32,
352 pub particle_lifetime_min: f32,
354 pub particle_lifetime_max: f32,
356 pub initial_velocity_min: f32,
358 pub initial_velocity_max: f32,
360 pub velocity_spread: f32,
362 pub gravity: Vec3,
364 pub drag: f32,
366 pub size_start: f32,
368 pub size_end: f32,
370 pub color_gradient: ColorGradient,
372 pub emissive_strength: f32,
374 pub enabled: bool,
376 pub accumulated_spawn: f32,
378 pub one_shot: bool,
380 pub has_fired: bool,
382 pub turbulence_strength: f32,
384 pub turbulence_frequency: f32,
386 pub texture_index: u32,
388 #[serde(default)]
392 pub size_curve: Vec<(f32, f32)>,
393 #[serde(default)]
397 pub opacity_curve: Vec<(f32, f32)>,
398 #[serde(default)]
401 pub death_sub_emitter: Option<String>,
402 #[serde(default)]
405 pub trail_sub_emitter: Option<String>,
406 #[serde(default)]
407 pub trail_spawn_interval: f32,
408}
409
410impl Default for ParticleEmitter {
411 fn default() -> Self {
412 Self {
413 emitter_type: EmitterType::Fire,
414 shape: EmitterShape::Point,
415 position: Vec3::zeros(),
416 direction: Vec3::new(0.0, 1.0, 0.0),
417 spawn_rate: 100.0,
418 burst_count: 0,
419 particle_lifetime_min: 1.0,
420 particle_lifetime_max: 2.0,
421 initial_velocity_min: 1.0,
422 initial_velocity_max: 3.0,
423 velocity_spread: 0.3,
424 gravity: Vec3::new(0.0, -9.81, 0.0),
425 drag: 0.1,
426 size_start: 0.1,
427 size_end: 0.0,
428 color_gradient: ColorGradient::default(),
429 emissive_strength: 1.0,
430 enabled: true,
431 accumulated_spawn: 0.0,
432 one_shot: false,
433 has_fired: false,
434 turbulence_strength: 0.0,
435 turbulence_frequency: 1.0,
436 texture_index: 0,
437 size_curve: Vec::new(),
438 opacity_curve: Vec::new(),
439 death_sub_emitter: None,
440 trail_sub_emitter: None,
441 trail_spawn_interval: 0.0,
442 }
443 }
444}
445
446impl ParticleEmitter {
447 pub fn firework_shell(position: Vec3, velocity: Vec3) -> Self {
449 Self {
450 emitter_type: EmitterType::Firework,
451 shape: EmitterShape::Point,
452 position,
453 direction: velocity.normalize(),
454 spawn_rate: 120.0,
455 burst_count: 0,
456 particle_lifetime_min: 0.15,
457 particle_lifetime_max: 0.4,
458 initial_velocity_min: velocity.magnitude() * 0.05,
459 initial_velocity_max: velocity.magnitude() * 0.15,
460 velocity_spread: 0.25,
461 gravity: Vec3::new(0.0, -4.0, 0.0),
462 drag: 0.1,
463 size_start: 0.15,
464 size_end: 0.03,
465 color_gradient: ColorGradient::firework_trail(),
466 emissive_strength: 5.0,
467 enabled: true,
468 accumulated_spawn: 0.0,
469 one_shot: false,
470 has_fired: false,
471 turbulence_strength: 0.2,
472 turbulence_frequency: 2.0,
473 texture_index: 0,
474 size_curve: Vec::new(),
475 opacity_curve: Vec::new(),
476 death_sub_emitter: None,
477 trail_sub_emitter: None,
478 trail_spawn_interval: 0.0,
479 }
480 }
481
482 pub fn firework_explosion(position: Vec3, color: Vec3, particle_count: u32) -> Self {
484 Self {
485 emitter_type: EmitterType::Firework,
486 shape: EmitterShape::Sphere { radius: 0.5 },
487 position,
488 direction: Vec3::new(0.0, 1.0, 0.0),
489 spawn_rate: 0.0,
490 burst_count: particle_count,
491 particle_lifetime_min: 2.0,
492 particle_lifetime_max: 3.5,
493 initial_velocity_min: 12.0,
494 initial_velocity_max: 25.0,
495 velocity_spread: std::f32::consts::PI,
496 gravity: Vec3::new(0.0, -4.0, 0.0),
497 drag: 0.4,
498 size_start: 0.25,
499 size_end: 0.04,
500 color_gradient: ColorGradient::firework_explosion(color),
501 emissive_strength: 8.0,
502 enabled: true,
503 accumulated_spawn: 0.0,
504 one_shot: true,
505 has_fired: false,
506 turbulence_strength: 0.3,
507 turbulence_frequency: 1.5,
508 texture_index: 0,
509 size_curve: Vec::new(),
510 opacity_curve: Vec::new(),
511 death_sub_emitter: None,
512 trail_sub_emitter: None,
513 trail_spawn_interval: 0.0,
514 }
515 }
516
517 pub fn firework_willow(position: Vec3, color: Vec3, particle_count: u32) -> Self {
518 Self {
519 emitter_type: EmitterType::Firework,
520 shape: EmitterShape::Sphere { radius: 0.3 },
521 position,
522 direction: Vec3::new(0.0, 1.0, 0.0),
523 spawn_rate: 0.0,
524 burst_count: particle_count,
525 particle_lifetime_min: 3.5,
526 particle_lifetime_max: 5.5,
527 initial_velocity_min: 8.0,
528 initial_velocity_max: 18.0,
529 velocity_spread: std::f32::consts::PI,
530 gravity: Vec3::new(0.0, -6.0, 0.0),
531 drag: 0.15,
532 size_start: 0.18,
533 size_end: 0.02,
534 color_gradient: ColorGradient::firework_willow(color),
535 emissive_strength: 6.0,
536 enabled: true,
537 accumulated_spawn: 0.0,
538 one_shot: true,
539 has_fired: false,
540 turbulence_strength: 0.1,
541 turbulence_frequency: 0.8,
542 texture_index: 0,
543 size_curve: Vec::new(),
544 opacity_curve: Vec::new(),
545 death_sub_emitter: None,
546 trail_sub_emitter: None,
547 trail_spawn_interval: 0.0,
548 }
549 }
550
551 pub fn firework_chrysanthemum(position: Vec3, color: Vec3, particle_count: u32) -> Self {
552 Self {
553 emitter_type: EmitterType::Firework,
554 shape: EmitterShape::Sphere { radius: 0.2 },
555 position,
556 direction: Vec3::new(0.0, 1.0, 0.0),
557 spawn_rate: 0.0,
558 burst_count: particle_count,
559 particle_lifetime_min: 2.5,
560 particle_lifetime_max: 4.0,
561 initial_velocity_min: 15.0,
562 initial_velocity_max: 30.0,
563 velocity_spread: std::f32::consts::PI,
564 gravity: Vec3::new(0.0, -2.5, 0.0),
565 drag: 0.6,
566 size_start: 0.2,
567 size_end: 0.05,
568 color_gradient: ColorGradient::firework_explosion(color),
569 emissive_strength: 10.0,
570 enabled: true,
571 accumulated_spawn: 0.0,
572 one_shot: true,
573 has_fired: false,
574 turbulence_strength: 0.15,
575 turbulence_frequency: 1.2,
576 texture_index: 0,
577 size_curve: Vec::new(),
578 opacity_curve: Vec::new(),
579 death_sub_emitter: None,
580 trail_sub_emitter: None,
581 trail_spawn_interval: 0.0,
582 }
583 }
584
585 pub fn firework_ring(position: Vec3, color: Vec3, particle_count: u32) -> Self {
586 Self {
587 emitter_type: EmitterType::Firework,
588 shape: EmitterShape::Point,
589 position,
590 direction: Vec3::new(0.0, 0.0, 1.0),
591 spawn_rate: 0.0,
592 burst_count: particle_count,
593 particle_lifetime_min: 2.0,
594 particle_lifetime_max: 3.0,
595 initial_velocity_min: 18.0,
596 initial_velocity_max: 22.0,
597 velocity_spread: 0.15,
598 gravity: Vec3::new(0.0, -1.5, 0.0),
599 drag: 0.35,
600 size_start: 0.22,
601 size_end: 0.03,
602 color_gradient: ColorGradient::firework_explosion(color),
603 emissive_strength: 9.0,
604 enabled: true,
605 accumulated_spawn: 0.0,
606 one_shot: true,
607 has_fired: false,
608 turbulence_strength: 0.05,
609 turbulence_frequency: 1.0,
610 texture_index: 0,
611 size_curve: Vec::new(),
612 opacity_curve: Vec::new(),
613 death_sub_emitter: None,
614 trail_sub_emitter: None,
615 trail_spawn_interval: 0.0,
616 }
617 }
618
619 pub fn firework_glitter(position: Vec3, particle_count: u32) -> Self {
620 Self {
621 emitter_type: EmitterType::Sparks,
622 shape: EmitterShape::Sphere { radius: 2.0 },
623 position,
624 direction: Vec3::new(0.0, 1.0, 0.0),
625 spawn_rate: 0.0,
626 burst_count: particle_count,
627 particle_lifetime_min: 0.8,
628 particle_lifetime_max: 2.5,
629 initial_velocity_min: 1.0,
630 initial_velocity_max: 5.0,
631 velocity_spread: std::f32::consts::PI,
632 gravity: Vec3::new(0.0, -8.0, 0.0),
633 drag: 0.05,
634 size_start: 0.12,
635 size_end: 0.02,
636 color_gradient: ColorGradient::firework_glitter(),
637 emissive_strength: 12.0,
638 enabled: true,
639 accumulated_spawn: 0.0,
640 one_shot: true,
641 has_fired: false,
642 turbulence_strength: 0.8,
643 turbulence_frequency: 4.0,
644 texture_index: 0,
645 size_curve: Vec::new(),
646 opacity_curve: Vec::new(),
647 death_sub_emitter: None,
648 trail_sub_emitter: None,
649 trail_spawn_interval: 0.0,
650 }
651 }
652
653 pub fn firework_crackle(position: Vec3, particle_count: u32) -> Self {
654 Self {
655 emitter_type: EmitterType::Sparks,
656 shape: EmitterShape::Sphere { radius: 1.5 },
657 position,
658 direction: Vec3::new(0.0, 1.0, 0.0),
659 spawn_rate: 0.0,
660 burst_count: particle_count,
661 particle_lifetime_min: 0.3,
662 particle_lifetime_max: 0.8,
663 initial_velocity_min: 3.0,
664 initial_velocity_max: 10.0,
665 velocity_spread: std::f32::consts::PI,
666 gravity: Vec3::new(0.0, -12.0, 0.0),
667 drag: 0.02,
668 size_start: 0.08,
669 size_end: 0.01,
670 color_gradient: ColorGradient::firework_crackle(),
671 emissive_strength: 15.0,
672 enabled: true,
673 accumulated_spawn: 0.0,
674 one_shot: true,
675 has_fired: false,
676 turbulence_strength: 1.2,
677 turbulence_frequency: 6.0,
678 texture_index: 0,
679 size_curve: Vec::new(),
680 opacity_curve: Vec::new(),
681 death_sub_emitter: None,
682 trail_sub_emitter: None,
683 trail_spawn_interval: 0.0,
684 }
685 }
686
687 pub fn flash_burst(position: Vec3) -> Self {
688 Self {
689 emitter_type: EmitterType::Firework,
690 shape: EmitterShape::Point,
691 position,
692 direction: Vec3::new(0.0, 1.0, 0.0),
693 spawn_rate: 0.0,
694 burst_count: 50,
695 particle_lifetime_min: 0.1,
696 particle_lifetime_max: 0.25,
697 initial_velocity_min: 2.0,
698 initial_velocity_max: 8.0,
699 velocity_spread: std::f32::consts::PI,
700 gravity: Vec3::new(0.0, 0.0, 0.0),
701 drag: 0.0,
702 size_start: 1.5,
703 size_end: 0.3,
704 color_gradient: ColorGradient::flash_burst(),
705 emissive_strength: 25.0,
706 enabled: true,
707 accumulated_spawn: 0.0,
708 one_shot: true,
709 has_fired: false,
710 turbulence_strength: 0.0,
711 turbulence_frequency: 0.0,
712 texture_index: 0,
713 size_curve: Vec::new(),
714 opacity_curve: Vec::new(),
715 death_sub_emitter: None,
716 trail_sub_emitter: None,
717 trail_spawn_interval: 0.0,
718 }
719 }
720
721 pub fn comet_shell(position: Vec3, velocity: Vec3) -> Self {
722 Self {
723 emitter_type: EmitterType::Firework,
724 shape: EmitterShape::Point,
725 position,
726 direction: velocity.normalize(),
727 spawn_rate: 200.0,
728 burst_count: 0,
729 particle_lifetime_min: 0.2,
730 particle_lifetime_max: 0.5,
731 initial_velocity_min: velocity.magnitude() * 0.02,
732 initial_velocity_max: velocity.magnitude() * 0.08,
733 velocity_spread: 0.15,
734 gravity: Vec3::new(0.0, -2.0, 0.0),
735 drag: 0.05,
736 size_start: 0.25,
737 size_end: 0.04,
738 color_gradient: ColorGradient::comet_trail(),
739 emissive_strength: 8.0,
740 enabled: true,
741 accumulated_spawn: 0.0,
742 one_shot: false,
743 has_fired: false,
744 turbulence_strength: 0.1,
745 turbulence_frequency: 1.5,
746 texture_index: 0,
747 size_curve: Vec::new(),
748 opacity_curve: Vec::new(),
749 death_sub_emitter: None,
750 trail_sub_emitter: None,
751 trail_spawn_interval: 0.0,
752 }
753 }
754
755 pub fn palm_explosion(position: Vec3, color: Vec3, particle_count: u32) -> Self {
756 Self {
757 emitter_type: EmitterType::Firework,
758 shape: EmitterShape::Sphere { radius: 0.2 },
759 position,
760 direction: Vec3::new(0.0, 1.0, 0.0),
761 spawn_rate: 0.0,
762 burst_count: particle_count,
763 particle_lifetime_min: 3.0,
764 particle_lifetime_max: 5.0,
765 initial_velocity_min: 20.0,
766 initial_velocity_max: 35.0,
767 velocity_spread: 0.4,
768 gravity: Vec3::new(0.0, -8.0, 0.0),
769 drag: 0.25,
770 size_start: 0.22,
771 size_end: 0.03,
772 color_gradient: ColorGradient::palm_frond(color),
773 emissive_strength: 8.0,
774 enabled: true,
775 accumulated_spawn: 0.0,
776 one_shot: true,
777 has_fired: false,
778 turbulence_strength: 0.2,
779 turbulence_frequency: 1.0,
780 texture_index: 0,
781 size_curve: Vec::new(),
782 opacity_curve: Vec::new(),
783 death_sub_emitter: None,
784 trail_sub_emitter: None,
785 trail_spawn_interval: 0.0,
786 }
787 }
788
789 pub fn crossette_burst(position: Vec3, color: Vec3, particle_count: u32) -> Self {
790 Self {
791 emitter_type: EmitterType::Sparks,
792 shape: EmitterShape::Sphere { radius: 0.1 },
793 position,
794 direction: Vec3::new(0.0, 1.0, 0.0),
795 spawn_rate: 0.0,
796 burst_count: particle_count,
797 particle_lifetime_min: 0.5,
798 particle_lifetime_max: 1.2,
799 initial_velocity_min: 15.0,
800 initial_velocity_max: 25.0,
801 velocity_spread: std::f32::consts::PI,
802 gravity: Vec3::new(0.0, -5.0, 0.0),
803 drag: 0.3,
804 size_start: 0.15,
805 size_end: 0.02,
806 color_gradient: ColorGradient::firework_explosion(color),
807 emissive_strength: 12.0,
808 enabled: true,
809 accumulated_spawn: 0.0,
810 one_shot: true,
811 has_fired: false,
812 turbulence_strength: 0.5,
813 turbulence_frequency: 3.0,
814 texture_index: 0,
815 size_curve: Vec::new(),
816 opacity_curve: Vec::new(),
817 death_sub_emitter: None,
818 trail_sub_emitter: None,
819 trail_spawn_interval: 0.0,
820 }
821 }
822
823 pub fn strobe_effect(position: Vec3, particle_count: u32) -> Self {
824 Self {
825 emitter_type: EmitterType::Sparks,
826 shape: EmitterShape::Sphere { radius: 3.0 },
827 position,
828 direction: Vec3::new(0.0, -1.0, 0.0),
829 spawn_rate: 0.0,
830 burst_count: particle_count,
831 particle_lifetime_min: 1.5,
832 particle_lifetime_max: 3.0,
833 initial_velocity_min: 0.5,
834 initial_velocity_max: 2.0,
835 velocity_spread: std::f32::consts::PI,
836 gravity: Vec3::new(0.0, -6.0, 0.0),
837 drag: 0.1,
838 size_start: 0.1,
839 size_end: 0.02,
840 color_gradient: ColorGradient::strobe(),
841 emissive_strength: 20.0,
842 enabled: true,
843 accumulated_spawn: 0.0,
844 one_shot: true,
845 has_fired: false,
846 turbulence_strength: 0.3,
847 turbulence_frequency: 2.0,
848 texture_index: 0,
849 size_curve: Vec::new(),
850 opacity_curve: Vec::new(),
851 death_sub_emitter: None,
852 trail_sub_emitter: None,
853 trail_spawn_interval: 0.0,
854 }
855 }
856
857 pub fn trailing_sparks(position: Vec3, velocity: Vec3) -> Self {
858 Self {
859 emitter_type: EmitterType::Sparks,
860 shape: EmitterShape::Point,
861 position,
862 direction: (-velocity).normalize(),
863 spawn_rate: 80.0,
864 burst_count: 0,
865 particle_lifetime_min: 0.2,
866 particle_lifetime_max: 0.6,
867 initial_velocity_min: 1.0,
868 initial_velocity_max: 4.0,
869 velocity_spread: 0.5,
870 gravity: Vec3::new(0.0, -15.0, 0.0),
871 drag: 0.05,
872 size_start: 0.06,
873 size_end: 0.01,
874 color_gradient: ColorGradient::firework_glitter(),
875 emissive_strength: 10.0,
876 enabled: true,
877 accumulated_spawn: 0.0,
878 one_shot: false,
879 has_fired: false,
880 turbulence_strength: 0.8,
881 turbulence_frequency: 5.0,
882 texture_index: 0,
883 size_curve: Vec::new(),
884 opacity_curve: Vec::new(),
885 death_sub_emitter: None,
886 trail_sub_emitter: None,
887 trail_spawn_interval: 0.0,
888 }
889 }
890
891 pub fn fire(position: Vec3) -> Self {
893 Self {
894 emitter_type: EmitterType::Fire,
895 shape: EmitterShape::Cone {
896 angle: 0.3,
897 height: 0.1,
898 },
899 position,
900 direction: Vec3::new(0.0, 1.0, 0.0),
901 spawn_rate: 200.0,
902 burst_count: 0,
903 particle_lifetime_min: 0.5,
904 particle_lifetime_max: 1.5,
905 initial_velocity_min: 2.0,
906 initial_velocity_max: 5.0,
907 velocity_spread: 0.4,
908 gravity: Vec3::new(0.0, 2.0, 0.0),
909 drag: 0.5,
910 size_start: 0.3,
911 size_end: 0.1,
912 color_gradient: ColorGradient::fire(),
913 emissive_strength: 4.0,
914 enabled: true,
915 accumulated_spawn: 0.0,
916 one_shot: false,
917 has_fired: false,
918 turbulence_strength: 1.5,
919 turbulence_frequency: 3.0,
920 texture_index: 0,
921 size_curve: Vec::new(),
922 opacity_curve: Vec::new(),
923 death_sub_emitter: None,
924 trail_sub_emitter: None,
925 trail_spawn_interval: 0.0,
926 }
927 }
928
929 pub fn smoke(position: Vec3) -> Self {
931 Self {
932 emitter_type: EmitterType::Smoke,
933 shape: EmitterShape::Cone {
934 angle: 0.1,
935 height: 0.2,
936 },
937 position,
938 direction: Vec3::new(0.0, 1.0, 0.0),
939 spawn_rate: 50.0,
940 burst_count: 0,
941 particle_lifetime_min: 2.5,
942 particle_lifetime_max: 4.5,
943 initial_velocity_min: 0.8,
944 initial_velocity_max: 1.8,
945 velocity_spread: 0.15,
946 gravity: Vec3::new(0.0, 0.6, 0.0),
947 drag: 0.3,
948 size_start: 0.4,
949 size_end: 1.2,
950 color_gradient: ColorGradient::smoke(),
951 emissive_strength: 0.0,
952 enabled: true,
953 accumulated_spawn: 0.0,
954 one_shot: false,
955 has_fired: false,
956 turbulence_strength: 0.3,
957 turbulence_frequency: 0.4,
958 texture_index: 0,
959 size_curve: Vec::new(),
960 opacity_curve: Vec::new(),
961 death_sub_emitter: None,
962 trail_sub_emitter: None,
963 trail_spawn_interval: 0.0,
964 }
965 }
966
967 pub fn sparks(position: Vec3) -> Self {
969 Self {
970 emitter_type: EmitterType::Sparks,
971 shape: EmitterShape::Point,
972 position,
973 direction: Vec3::new(0.0, 1.0, 0.0),
974 spawn_rate: 50.0,
975 burst_count: 0,
976 particle_lifetime_min: 0.3,
977 particle_lifetime_max: 0.8,
978 initial_velocity_min: 5.0,
979 initial_velocity_max: 10.0,
980 velocity_spread: 0.8,
981 gravity: Vec3::new(0.0, -15.0, 0.0),
982 drag: 0.1,
983 size_start: 0.05,
984 size_end: 0.01,
985 color_gradient: ColorGradient::sparks(),
986 emissive_strength: 6.0,
987 enabled: true,
988 accumulated_spawn: 0.0,
989 one_shot: false,
990 has_fired: false,
991 turbulence_strength: 0.0,
992 turbulence_frequency: 1.0,
993 texture_index: 0,
994 size_curve: Vec::new(),
995 opacity_curve: Vec::new(),
996 death_sub_emitter: None,
997 trail_sub_emitter: None,
998 trail_spawn_interval: 0.0,
999 }
1000 }
1001}
1002
1003#[derive(Debug, Clone, Copy, Default)]
1005pub struct ParticleSystemStats {
1006 pub active_particles: u32,
1008 pub total_spawned: u64,
1010 pub total_died: u64,
1012}