nightshade 0.13.3

A cross-platform data-oriented game engine.
Documentation
use nalgebra_glm::{Vec3, Vec4};
use serde::{Deserialize, Serialize};

use crate::ecs::particles::components::{
    ColorGradient, EmitterShape, EmitterType, ParticleEmitter,
};

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
pub enum SceneEmitterType {
    Firework,
    #[default]
    Fire,
    Smoke,
    Sparks,
    Trail,
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
pub enum SceneEmitterShape {
    #[default]
    Point,
    Sphere {
        radius: f32,
    },
    Cone {
        angle: f32,
        height: f32,
    },
    Box {
        half_extents: [f32; 3],
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneColorGradient {
    pub colors: Vec<(f32, [f32; 4])>,
}

impl Default for SceneColorGradient {
    fn default() -> Self {
        Self {
            colors: vec![(0.0, [1.0, 1.0, 1.0, 1.0]), (1.0, [1.0, 1.0, 1.0, 0.0])],
        }
    }
}

impl From<&ColorGradient> for SceneColorGradient {
    fn from(gradient: &ColorGradient) -> Self {
        Self {
            colors: gradient
                .colors
                .iter()
                .map(|(time, color)| (*time, [color.x, color.y, color.z, color.w]))
                .collect(),
        }
    }
}

impl SceneColorGradient {
    pub fn to_color_gradient(&self) -> ColorGradient {
        ColorGradient {
            colors: self
                .colors
                .iter()
                .map(|(time, color)| (*time, Vec4::new(color[0], color[1], color[2], color[3])))
                .collect(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct SceneParticleEmitter {
    pub emitter_type: SceneEmitterType,
    pub shape: SceneEmitterShape,
    pub position: [f32; 3],
    pub direction: [f32; 3],
    pub spawn_rate: f32,
    pub burst_count: u32,
    pub particle_lifetime_min: f32,
    pub particle_lifetime_max: f32,
    pub initial_velocity_min: f32,
    pub initial_velocity_max: f32,
    pub velocity_spread: f32,
    pub gravity: [f32; 3],
    pub drag: f32,
    pub size_start: f32,
    pub size_end: f32,
    pub color_gradient: SceneColorGradient,
    pub emissive_strength: f32,
    pub enabled: bool,
    pub one_shot: bool,
    pub turbulence_strength: f32,
    pub turbulence_frequency: f32,
    pub texture_index: u32,
}

impl Default for SceneParticleEmitter {
    fn default() -> Self {
        Self {
            emitter_type: SceneEmitterType::Fire,
            shape: SceneEmitterShape::Point,
            position: [0.0, 0.0, 0.0],
            direction: [0.0, 1.0, 0.0],
            spawn_rate: 100.0,
            burst_count: 0,
            particle_lifetime_min: 1.0,
            particle_lifetime_max: 2.0,
            initial_velocity_min: 1.0,
            initial_velocity_max: 3.0,
            velocity_spread: 0.3,
            gravity: [0.0, -9.81, 0.0],
            drag: 0.1,
            size_start: 0.1,
            size_end: 0.0,
            color_gradient: SceneColorGradient::default(),
            emissive_strength: 1.0,
            enabled: true,
            one_shot: false,
            turbulence_strength: 0.0,
            turbulence_frequency: 1.0,
            texture_index: 0,
        }
    }
}

impl From<&ParticleEmitter> for SceneParticleEmitter {
    fn from(emitter: &ParticleEmitter) -> Self {
        Self {
            emitter_type: match emitter.emitter_type {
                EmitterType::Firework => SceneEmitterType::Firework,
                EmitterType::Fire => SceneEmitterType::Fire,
                EmitterType::Smoke => SceneEmitterType::Smoke,
                EmitterType::Sparks => SceneEmitterType::Sparks,
                EmitterType::Trail => SceneEmitterType::Trail,
            },
            shape: match emitter.shape {
                EmitterShape::Point => SceneEmitterShape::Point,
                EmitterShape::Sphere { radius } => SceneEmitterShape::Sphere { radius },
                EmitterShape::Cone { angle, height } => SceneEmitterShape::Cone { angle, height },
                EmitterShape::Box { half_extents } => SceneEmitterShape::Box {
                    half_extents: [half_extents.x, half_extents.y, half_extents.z],
                },
            },
            position: [emitter.position.x, emitter.position.y, emitter.position.z],
            direction: [
                emitter.direction.x,
                emitter.direction.y,
                emitter.direction.z,
            ],
            spawn_rate: emitter.spawn_rate,
            burst_count: emitter.burst_count,
            particle_lifetime_min: emitter.particle_lifetime_min,
            particle_lifetime_max: emitter.particle_lifetime_max,
            initial_velocity_min: emitter.initial_velocity_min,
            initial_velocity_max: emitter.initial_velocity_max,
            velocity_spread: emitter.velocity_spread,
            gravity: [emitter.gravity.x, emitter.gravity.y, emitter.gravity.z],
            drag: emitter.drag,
            size_start: emitter.size_start,
            size_end: emitter.size_end,
            color_gradient: SceneColorGradient::from(&emitter.color_gradient),
            emissive_strength: emitter.emissive_strength,
            enabled: emitter.enabled,
            one_shot: emitter.one_shot,
            turbulence_strength: emitter.turbulence_strength,
            turbulence_frequency: emitter.turbulence_frequency,
            texture_index: emitter.texture_index,
        }
    }
}

impl SceneParticleEmitter {
    pub fn to_particle_emitter(&self) -> ParticleEmitter {
        ParticleEmitter {
            emitter_type: match self.emitter_type {
                SceneEmitterType::Firework => EmitterType::Firework,
                SceneEmitterType::Fire => EmitterType::Fire,
                SceneEmitterType::Smoke => EmitterType::Smoke,
                SceneEmitterType::Sparks => EmitterType::Sparks,
                SceneEmitterType::Trail => EmitterType::Trail,
            },
            shape: match self.shape {
                SceneEmitterShape::Point => EmitterShape::Point,
                SceneEmitterShape::Sphere { radius } => EmitterShape::Sphere { radius },
                SceneEmitterShape::Cone { angle, height } => EmitterShape::Cone { angle, height },
                SceneEmitterShape::Box { half_extents } => EmitterShape::Box {
                    half_extents: Vec3::new(half_extents[0], half_extents[1], half_extents[2]),
                },
            },
            position: Vec3::new(self.position[0], self.position[1], self.position[2]),
            direction: Vec3::new(self.direction[0], self.direction[1], self.direction[2]),
            spawn_rate: self.spawn_rate,
            burst_count: self.burst_count,
            particle_lifetime_min: self.particle_lifetime_min,
            particle_lifetime_max: self.particle_lifetime_max,
            initial_velocity_min: self.initial_velocity_min,
            initial_velocity_max: self.initial_velocity_max,
            velocity_spread: self.velocity_spread,
            gravity: Vec3::new(self.gravity[0], self.gravity[1], self.gravity[2]),
            drag: self.drag,
            size_start: self.size_start,
            size_end: self.size_end,
            color_gradient: self.color_gradient.to_color_gradient(),
            emissive_strength: self.emissive_strength,
            enabled: self.enabled,
            accumulated_spawn: 0.0,
            one_shot: self.one_shot,
            has_fired: false,
            turbulence_strength: self.turbulence_strength,
            turbulence_frequency: self.turbulence_frequency,
            texture_index: self.texture_index,
        }
    }
}