Gunnar

mikronova/kyokkou

src/art_scenes.rs

use crate::common::{Scene, ValidationStatus, render_button, show_validation};

#[derive(Debug, Clone, PartialEq)]
pub enum MetalType {
    Gold,
    Steel,
    Chrome,
    Copper,
}

#[derive(Debug, Clone, PartialEq)]
pub enum SceneType {
    GodRays,
    MetalReflections,
    WaterLiquid,
    FluidPipes,
    VolumetricClouds,
}

pub struct ArtScenes {
    pub scene_type: SceneType,
    pub metal: MetalType,
    pub render_samples: u32,
    pub logo_path: String,
}

impl Default for ArtScenes {
    fn default() -> Self {
        Self {
            scene_type: SceneType::GodRays,
            metal: MetalType::Chrome,
            render_samples: 128,
            logo_path: String::new(),
        }
    }
}

impl ArtScenes {
    fn to_json(&self) -> String {
        let scene_type = match self.scene_type {
            SceneType::GodRays          => "god_rays",
            SceneType::MetalReflections => "metal_reflections",
            SceneType::WaterLiquid      => "water_liquid",
            SceneType::FluidPipes       => "fluid_pipes",
            SceneType::VolumetricClouds => "volumetric_clouds",
        };
        let metal = match self.metal {
            MetalType::Gold   => "gold",
            MetalType::Steel  => "steel",
            MetalType::Chrome => "chrome",
            MetalType::Copper => "copper",
        };
        let samples = if matches!(self.scene_type, SceneType::VolumetricClouds) {
            self.render_samples.max(512)
        } else {
            self.render_samples
        };
        let logo = if self.logo_path.is_empty() {
            "null".to_string()
        } else {
            "\"/in/textures/logo.png\"".to_string()
        };
        format!(
            r#"{{"scene_type":"{scene_type}","metal":"{metal}","render_samples":{samples},"output_path":"/out/render.png","logo_path":{logo}}}"#
        )
    }
}

impl Scene for ArtScenes {
    fn name(&self) -> &str {
        "Art scenes"
    }

    fn validate(&self) -> Vec<ValidationStatus> {
        let mut v = vec![];
        if matches!(self.scene_type, SceneType::VolumetricClouds) && self.render_samples < 512 {
            v.push(ValidationStatus::Warning(
                "Volume clouds: samples auto-upgraded to 512".into(),
            ));
        }
        if self.render_samples == 0 {
            v.push(ValidationStatus::Error("Render samples must be > 0".into()));
        }
        if v.is_empty() {
            v.push(ValidationStatus::Ok);
        }
        v
    }

    fn show(&mut self, ui: &mut egui::Ui) -> Option<String> {
        ui.heading("Art scenes");
        ui.separator();

        ui.horizontal(|ui| {
            ui.label("Scene:");
            egui::ComboBox::from_id_salt("scene_type")
                .selected_text(format!("{:?}", self.scene_type))
                .show_ui(ui, |ui| {
                    ui.selectable_value(&mut self.scene_type, SceneType::GodRays, "God rays");
                    ui.selectable_value(&mut self.scene_type, SceneType::MetalReflections, "Metal reflections");
                    ui.selectable_value(&mut self.scene_type, SceneType::WaterLiquid, "Water / liquid");
                    ui.selectable_value(&mut self.scene_type, SceneType::FluidPipes, "Fluid pipes");
                    ui.selectable_value(&mut self.scene_type, SceneType::VolumetricClouds, "Volumetric clouds");
                });
        });

        ui.horizontal(|ui| {
            ui.label("Metal:");
            egui::ComboBox::from_id_salt("metal_type")
                .selected_text(format!("{:?}", self.metal))
                .show_ui(ui, |ui| {
                    ui.selectable_value(&mut self.metal, MetalType::Gold, "Gold");
                    ui.selectable_value(&mut self.metal, MetalType::Steel, "Steel");
                    ui.selectable_value(&mut self.metal, MetalType::Chrome, "Chrome");
                    ui.selectable_value(&mut self.metal, MetalType::Copper, "Copper");
                });
        });

        ui.horizontal(|ui| {
            ui.label("Samples:");
            ui.add(egui::Slider::new(&mut self.render_samples, 1..=4096));
        });

        ui.horizontal(|ui| {
            ui.label("Logo path:");
            ui.text_edit_singleline(&mut self.logo_path);
        });

        ui.separator();
        let statuses = self.validate();
        show_validation(ui, &statuses);
        ui.separator();
        if render_button(ui, self.is_renderable()) {
            return Some(self.to_json());
        }
        None
    }
}