gled 2.28.5

gled is an application for creating animations and effects on artnet or dmx installations
pub mod color;
pub mod effect;
pub mod effect_state;
pub(crate) mod grid;
pub mod instance;

use super::{AssetTrait, animation::Animation, palette::Palette};
use crate::{
    audio::sound_data::SoundData,
    pipeline::group::{GroupIndices, Groups},
    storage::{AssetId, collections::Collections},
    ui::pills::show_pills,
};
use effect::Effect;
use egui::{Color32, Vec2};
use serde::{Deserialize, Serialize};
use wgpu::{CommandEncoder, Queue};

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct Scene {
    pub effects: Vec<Effect>,
}

impl Scene {
    pub fn group_indices(&self) -> GroupIndices {
        self.effects
            .iter()
            .map(|effect| effect.group_index)
            .collect()
    }

    pub fn effect(&mut self, index: usize) -> Option<&mut Effect> {
        self.effects.get_mut(index)
    }

    pub fn effects(&self) -> &[Effect] {
        &self.effects
    }

    pub fn add_effect(&mut self, mut effect: Effect, collections: &Collections) -> usize {
        // Initialize the GPU pipeline for this newly added effect so it renders
        // immediately at runtime (otherwise it only works after save/reload, when
        // `reload_shader_code` is invoked during scene loading).
        effect
            .state
            .set_shader_code(&effect.shader_code_complete(collections));
        self.effects.push(effect);

        self.effects.len() - 1
    }

    pub fn remove_effect(&mut self, index: usize) -> Option<Effect> {
        let mut effect = None;

        if self.effects.len() > index {
            effect = Some(self.effects.remove(index));
        }

        effect
    }

    /// Reload shader code for all effects using the given animation, should be called after an animation is edited
    /// If the given animation is None, reloads all effects
    pub fn reload_shader_code(
        &mut self,
        animation: Option<AssetId<Animation>>,
        collections: &Collections,
    ) {
        for effect in self.effects.iter_mut() {
            if let Some(animation) = animation
                && effect.animation == Some(animation)
            {
                continue;
            }

            effect
                .state
                .set_shader_code(&effect.shader_code_complete(collections));
        }
    }

    pub fn prepare(
        &mut self,
        queue: &Queue,
        palette: Option<Palette>,
        groups: &Groups,
        main_opacity: f32,
        collections: &Collections,
        sound_data: &SoundData,
    ) {
        for effect in self.effects.iter_mut() {
            effect.prepare(
                queue,
                palette.clone(),
                groups,
                main_opacity,
                collections,
                sound_data,
            );
        }
    }

    #[cfg_attr(feature = "profiling", profiling::function)]
    pub fn render(&mut self, encoder: &mut CommandEncoder, send_output: bool) {
        for effect in self.effects.iter_mut() {
            effect.render(encoder, send_output);
        }
    }
}

impl AssetTrait for Scene {
    const DIR_NAME: &'static str = "scenes";
    const NAME: &'static str = "Scene";
    const SHOW_NAME_IF_SELECTED: bool = true;

    fn show(&self, ui: &mut egui::Ui, rect: egui::Rect) {
        show_pills(
            ui,
            rect.right_top() + Vec2::new(0.0, 1.0),
            self.group_indices()
                .into_iter()
                .map(|index| (index.to_string(), Color32::GOLD))
                .collect(),
        );
    }
}