nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use super::{ComponentInspector, InspectorContext, impl_simple_inspector};
use crate::ecs::particles::components::{EmitterShape, EmitterType, ParticleEmitter};
use crate::prelude::*;

fn add_particle_emitter(world: &mut World, entity: Entity) {
    world.add_components(entity, crate::ecs::world::PARTICLE_EMITTER);
    world.set_particle_emitter(entity, ParticleEmitter::default());
}

fn particle_emitter_ui(
    world: &mut World,
    entity: Entity,
    ui: &mut egui::Ui,
    _context: &mut InspectorContext,
) {
    if let Some(emitter) = world.get_particle_emitter_mut(entity) {
        ui.horizontal(|ui| {
            ui.label("Enabled:");
            ui.checkbox(&mut emitter.enabled, "");
        });

        ui.horizontal(|ui| {
            ui.label("One Shot:");
            ui.checkbox(&mut emitter.one_shot, "");
        });

        ui.separator();
        ui.label(egui::RichText::new("Emitter Type").strong());

        let type_label = match emitter.emitter_type {
            EmitterType::Firework => "Firework",
            EmitterType::Fire => "Fire",
            EmitterType::Smoke => "Smoke",
            EmitterType::Sparks => "Sparks",
            EmitterType::Trail => "Trail",
        };
        egui::ComboBox::from_id_salt("emitter_type")
            .selected_text(type_label)
            .show_ui(ui, |ui| {
                ui.selectable_value(&mut emitter.emitter_type, EmitterType::Fire, "Fire");
                ui.selectable_value(&mut emitter.emitter_type, EmitterType::Smoke, "Smoke");
                ui.selectable_value(&mut emitter.emitter_type, EmitterType::Sparks, "Sparks");
                ui.selectable_value(&mut emitter.emitter_type, EmitterType::Trail, "Trail");
                ui.selectable_value(&mut emitter.emitter_type, EmitterType::Firework, "Firework");
            });

        ui.separator();
        ui.label(egui::RichText::new("Shape").strong());

        let shape_label = match emitter.shape {
            EmitterShape::Point => "Point",
            EmitterShape::Sphere { .. } => "Sphere",
            EmitterShape::Cone { .. } => "Cone",
            EmitterShape::Box { .. } => "Box",
        };
        let mut shape_index = match emitter.shape {
            EmitterShape::Point => 0,
            EmitterShape::Sphere { .. } => 1,
            EmitterShape::Cone { .. } => 2,
            EmitterShape::Box { .. } => 3,
        };
        let old_shape_index = shape_index;
        egui::ComboBox::from_id_salt("emitter_shape")
            .selected_text(shape_label)
            .show_ui(ui, |ui| {
                ui.selectable_value(&mut shape_index, 0, "Point");
                ui.selectable_value(&mut shape_index, 1, "Sphere");
                ui.selectable_value(&mut shape_index, 2, "Cone");
                ui.selectable_value(&mut shape_index, 3, "Box");
            });
        if shape_index != old_shape_index {
            emitter.shape = match shape_index {
                1 => EmitterShape::Sphere { radius: 1.0 },
                2 => EmitterShape::Cone {
                    angle: 0.5,
                    height: 1.0,
                },
                3 => EmitterShape::Box {
                    half_extents: nalgebra_glm::vec3(0.5, 0.5, 0.5),
                },
                _ => EmitterShape::Point,
            };
        }

        match &mut emitter.shape {
            EmitterShape::Sphere { radius } => {
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(egui::DragValue::new(radius).speed(0.1).range(0.01..=100.0));
                });
            }
            EmitterShape::Cone { angle, height } => {
                ui.horizontal(|ui| {
                    ui.label("Angle:");
                    ui.add(egui::Slider::new(angle, 0.01..=std::f32::consts::PI));
                });
                ui.horizontal(|ui| {
                    ui.label("Height:");
                    ui.add(egui::DragValue::new(height).speed(0.1).range(0.01..=100.0));
                });
            }
            EmitterShape::Box { half_extents } => {
                ui.horizontal(|ui| {
                    ui.label("Half Extents:");
                    ui.add(
                        egui::DragValue::new(&mut half_extents.x)
                            .speed(0.1)
                            .prefix("X: "),
                    );
                    ui.add(
                        egui::DragValue::new(&mut half_extents.y)
                            .speed(0.1)
                            .prefix("Y: "),
                    );
                    ui.add(
                        egui::DragValue::new(&mut half_extents.z)
                            .speed(0.1)
                            .prefix("Z: "),
                    );
                });
            }
            EmitterShape::Point => {}
        }

        ui.separator();
        ui.label(egui::RichText::new("Spawning").strong());

        ui.horizontal(|ui| {
            ui.label("Spawn Rate:");
            ui.add(
                egui::DragValue::new(&mut emitter.spawn_rate)
                    .speed(1.0)
                    .range(0.0..=10000.0),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Burst Count:");
            ui.add(egui::DragValue::new(&mut emitter.burst_count).speed(1.0));
        });

        ui.separator();
        ui.label(egui::RichText::new("Lifetime").strong());

        ui.horizontal(|ui| {
            ui.label("Min:");
            ui.add(
                egui::DragValue::new(&mut emitter.particle_lifetime_min)
                    .speed(0.1)
                    .range(0.01..=60.0),
            );
            ui.label("Max:");
            ui.add(
                egui::DragValue::new(&mut emitter.particle_lifetime_max)
                    .speed(0.1)
                    .range(0.01..=60.0),
            );
        });

        ui.separator();
        ui.label(egui::RichText::new("Velocity").strong());

        ui.horizontal(|ui| {
            ui.label("Min Speed:");
            ui.add(
                egui::DragValue::new(&mut emitter.initial_velocity_min)
                    .speed(0.1)
                    .range(0.0..=100.0),
            );
            ui.label("Max Speed:");
            ui.add(
                egui::DragValue::new(&mut emitter.initial_velocity_max)
                    .speed(0.1)
                    .range(0.0..=100.0),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Spread:");
            ui.add(egui::Slider::new(
                &mut emitter.velocity_spread,
                0.0..=std::f32::consts::PI,
            ));
        });

        ui.horizontal(|ui| {
            ui.label("Direction:");
            ui.add(
                egui::DragValue::new(&mut emitter.direction.x)
                    .speed(0.1)
                    .prefix("X: "),
            );
            ui.add(
                egui::DragValue::new(&mut emitter.direction.y)
                    .speed(0.1)
                    .prefix("Y: "),
            );
            ui.add(
                egui::DragValue::new(&mut emitter.direction.z)
                    .speed(0.1)
                    .prefix("Z: "),
            );
        });

        ui.separator();
        ui.label(egui::RichText::new("Physics").strong());

        ui.horizontal(|ui| {
            ui.label("Gravity:");
            ui.add(
                egui::DragValue::new(&mut emitter.gravity.x)
                    .speed(0.1)
                    .prefix("X: "),
            );
            ui.add(
                egui::DragValue::new(&mut emitter.gravity.y)
                    .speed(0.1)
                    .prefix("Y: "),
            );
            ui.add(
                egui::DragValue::new(&mut emitter.gravity.z)
                    .speed(0.1)
                    .prefix("Z: "),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Drag:");
            ui.add(egui::Slider::new(&mut emitter.drag, 0.0..=2.0));
        });

        ui.separator();
        ui.label(egui::RichText::new("Size").strong());

        ui.horizontal(|ui| {
            ui.label("Start:");
            ui.add(
                egui::DragValue::new(&mut emitter.size_start)
                    .speed(0.01)
                    .range(0.0..=10.0),
            );
            ui.label("End:");
            ui.add(
                egui::DragValue::new(&mut emitter.size_end)
                    .speed(0.01)
                    .range(0.0..=10.0),
            );
        });

        ui.separator();
        ui.label(egui::RichText::new("Rendering").strong());

        ui.horizontal(|ui| {
            ui.label("Emissive Strength:");
            ui.add(egui::Slider::new(
                &mut emitter.emissive_strength,
                0.0..=30.0,
            ));
        });

        ui.horizontal(|ui| {
            ui.label("Texture Index:");
            ui.add(egui::DragValue::new(&mut emitter.texture_index));
        });

        ui.separator();
        ui.label(egui::RichText::new("Turbulence").strong());

        ui.horizontal(|ui| {
            ui.label("Strength:");
            ui.add(egui::Slider::new(
                &mut emitter.turbulence_strength,
                0.0..=5.0,
            ));
        });

        ui.horizontal(|ui| {
            ui.label("Frequency:");
            ui.add(egui::Slider::new(
                &mut emitter.turbulence_frequency,
                0.01..=10.0,
            ));
        });
    }
}

impl_simple_inspector!(
    ParticleEmitterInspector,
    "Particle Emitter",
    entity_has_particle_emitter,
    remove_particle_emitter,
    particle_emitter_ui,
    custom_add => add_particle_emitter
);