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}