Skip to main content

nightshade/ecs/scene/
particles.rs

1use nalgebra_glm::{Vec3, Vec4};
2use serde::{Deserialize, Serialize};
3
4use crate::render::particles::{ColorGradient, EmitterShape, EmitterType, ParticleEmitter};
5
6#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
7pub enum SceneEmitterType {
8    Firework,
9    #[default]
10    Fire,
11    Smoke,
12    Sparks,
13    Trail,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
17pub enum SceneEmitterShape {
18    #[default]
19    Point,
20    Sphere {
21        radius: f32,
22    },
23    Cone {
24        angle: f32,
25        height: f32,
26    },
27    Box {
28        half_extents: [f32; 3],
29    },
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct SceneColorGradient {
34    pub colors: Vec<(f32, [f32; 4])>,
35}
36
37impl Default for SceneColorGradient {
38    fn default() -> Self {
39        Self {
40            colors: vec![(0.0, [1.0, 1.0, 1.0, 1.0]), (1.0, [1.0, 1.0, 1.0, 0.0])],
41        }
42    }
43}
44
45impl From<&ColorGradient> for SceneColorGradient {
46    fn from(gradient: &ColorGradient) -> Self {
47        Self {
48            colors: gradient
49                .colors
50                .iter()
51                .map(|(time, color)| (*time, [color.x, color.y, color.z, color.w]))
52                .collect(),
53        }
54    }
55}
56
57impl SceneColorGradient {
58    pub fn to_color_gradient(&self) -> ColorGradient {
59        ColorGradient {
60            colors: self
61                .colors
62                .iter()
63                .map(|(time, color)| (*time, Vec4::new(color[0], color[1], color[2], color[3])))
64                .collect(),
65        }
66    }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(default)]
71pub struct SceneParticleEmitter {
72    pub emitter_type: SceneEmitterType,
73    pub shape: SceneEmitterShape,
74    pub position: [f32; 3],
75    pub direction: [f32; 3],
76    pub spawn_rate: f32,
77    pub burst_count: u32,
78    pub particle_lifetime_min: f32,
79    pub particle_lifetime_max: f32,
80    pub initial_velocity_min: f32,
81    pub initial_velocity_max: f32,
82    pub velocity_spread: f32,
83    pub gravity: [f32; 3],
84    pub drag: f32,
85    pub size_start: f32,
86    pub size_end: f32,
87    pub color_gradient: SceneColorGradient,
88    pub emissive_strength: f32,
89    pub enabled: bool,
90    pub one_shot: bool,
91    pub turbulence_strength: f32,
92    pub turbulence_frequency: f32,
93    pub texture_index: u32,
94    #[serde(default)]
95    pub size_curve: Vec<(f32, f32)>,
96    #[serde(default)]
97    pub opacity_curve: Vec<(f32, f32)>,
98    #[serde(default)]
99    pub death_sub_emitter: Option<String>,
100    #[serde(default)]
101    pub trail_sub_emitter: Option<String>,
102    #[serde(default)]
103    pub trail_spawn_interval: f32,
104}
105
106impl Default for SceneParticleEmitter {
107    fn default() -> Self {
108        Self {
109            emitter_type: SceneEmitterType::Fire,
110            shape: SceneEmitterShape::Point,
111            position: [0.0, 0.0, 0.0],
112            direction: [0.0, 1.0, 0.0],
113            spawn_rate: 100.0,
114            burst_count: 0,
115            particle_lifetime_min: 1.0,
116            particle_lifetime_max: 2.0,
117            initial_velocity_min: 1.0,
118            initial_velocity_max: 3.0,
119            velocity_spread: 0.3,
120            gravity: [0.0, -9.81, 0.0],
121            drag: 0.1,
122            size_start: 0.1,
123            size_end: 0.0,
124            color_gradient: SceneColorGradient::default(),
125            emissive_strength: 1.0,
126            enabled: true,
127            one_shot: false,
128            turbulence_strength: 0.0,
129            turbulence_frequency: 1.0,
130            texture_index: 0,
131            size_curve: Vec::new(),
132            opacity_curve: Vec::new(),
133            death_sub_emitter: None,
134            trail_sub_emitter: None,
135            trail_spawn_interval: 0.0,
136        }
137    }
138}
139
140impl From<&ParticleEmitter> for SceneParticleEmitter {
141    fn from(emitter: &ParticleEmitter) -> Self {
142        Self {
143            emitter_type: match emitter.emitter_type {
144                EmitterType::Firework => SceneEmitterType::Firework,
145                EmitterType::Fire => SceneEmitterType::Fire,
146                EmitterType::Smoke => SceneEmitterType::Smoke,
147                EmitterType::Sparks => SceneEmitterType::Sparks,
148                EmitterType::Trail => SceneEmitterType::Trail,
149            },
150            shape: match emitter.shape {
151                EmitterShape::Point => SceneEmitterShape::Point,
152                EmitterShape::Sphere { radius } => SceneEmitterShape::Sphere { radius },
153                EmitterShape::Cone { angle, height } => SceneEmitterShape::Cone { angle, height },
154                EmitterShape::Box { half_extents } => SceneEmitterShape::Box {
155                    half_extents: [half_extents.x, half_extents.y, half_extents.z],
156                },
157            },
158            position: [emitter.position.x, emitter.position.y, emitter.position.z],
159            direction: [
160                emitter.direction.x,
161                emitter.direction.y,
162                emitter.direction.z,
163            ],
164            spawn_rate: emitter.spawn_rate,
165            burst_count: emitter.burst_count,
166            particle_lifetime_min: emitter.particle_lifetime_min,
167            particle_lifetime_max: emitter.particle_lifetime_max,
168            initial_velocity_min: emitter.initial_velocity_min,
169            initial_velocity_max: emitter.initial_velocity_max,
170            velocity_spread: emitter.velocity_spread,
171            gravity: [emitter.gravity.x, emitter.gravity.y, emitter.gravity.z],
172            drag: emitter.drag,
173            size_start: emitter.size_start,
174            size_end: emitter.size_end,
175            color_gradient: SceneColorGradient::from(&emitter.color_gradient),
176            emissive_strength: emitter.emissive_strength,
177            enabled: emitter.enabled,
178            one_shot: emitter.one_shot,
179            turbulence_strength: emitter.turbulence_strength,
180            turbulence_frequency: emitter.turbulence_frequency,
181            texture_index: emitter.texture_index,
182            size_curve: emitter.size_curve.clone(),
183            opacity_curve: emitter.opacity_curve.clone(),
184            death_sub_emitter: emitter.death_sub_emitter.clone(),
185            trail_sub_emitter: emitter.trail_sub_emitter.clone(),
186            trail_spawn_interval: emitter.trail_spawn_interval,
187        }
188    }
189}
190
191impl SceneParticleEmitter {
192    pub fn to_particle_emitter(&self) -> ParticleEmitter {
193        ParticleEmitter {
194            emitter_type: match self.emitter_type {
195                SceneEmitterType::Firework => EmitterType::Firework,
196                SceneEmitterType::Fire => EmitterType::Fire,
197                SceneEmitterType::Smoke => EmitterType::Smoke,
198                SceneEmitterType::Sparks => EmitterType::Sparks,
199                SceneEmitterType::Trail => EmitterType::Trail,
200            },
201            shape: match self.shape {
202                SceneEmitterShape::Point => EmitterShape::Point,
203                SceneEmitterShape::Sphere { radius } => EmitterShape::Sphere { radius },
204                SceneEmitterShape::Cone { angle, height } => EmitterShape::Cone { angle, height },
205                SceneEmitterShape::Box { half_extents } => EmitterShape::Box {
206                    half_extents: Vec3::new(half_extents[0], half_extents[1], half_extents[2]),
207                },
208            },
209            position: Vec3::new(self.position[0], self.position[1], self.position[2]),
210            direction: Vec3::new(self.direction[0], self.direction[1], self.direction[2]),
211            spawn_rate: self.spawn_rate,
212            burst_count: self.burst_count,
213            particle_lifetime_min: self.particle_lifetime_min,
214            particle_lifetime_max: self.particle_lifetime_max,
215            initial_velocity_min: self.initial_velocity_min,
216            initial_velocity_max: self.initial_velocity_max,
217            velocity_spread: self.velocity_spread,
218            gravity: Vec3::new(self.gravity[0], self.gravity[1], self.gravity[2]),
219            drag: self.drag,
220            size_start: self.size_start,
221            size_end: self.size_end,
222            color_gradient: self.color_gradient.to_color_gradient(),
223            emissive_strength: self.emissive_strength,
224            enabled: self.enabled,
225            accumulated_spawn: 0.0,
226            one_shot: self.one_shot,
227            has_fired: false,
228            turbulence_strength: self.turbulence_strength,
229            turbulence_frequency: self.turbulence_frequency,
230            texture_index: self.texture_index,
231            size_curve: self.size_curve.clone(),
232            opacity_curve: self.opacity_curve.clone(),
233            death_sub_emitter: self.death_sub_emitter.clone(),
234            trail_sub_emitter: self.trail_sub_emitter.clone(),
235            trail_spawn_interval: self.trail_spawn_interval,
236        }
237    }
238}