sevenx_engine 0.2.11

Engine de jogos 2D/3D completa com suporte Android, física, áudio, partículas, tilemap, UI, eventos e sistema 3D avançado com PBR.
Documentation
// Sistema de shaders customizáveis
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomShader {
    pub name: String,
    pub code: String,
    pub uniforms: HashMap<String, ShaderUniform>,
    compiled: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ShaderUniform {
    Float(f32),
    Vec2(f32, f32),
    Vec3(f32, f32, f32),
    Vec4(f32, f32, f32, f32),
    Color([u8; 4]),
    Texture(String),
}

impl CustomShader {
    pub fn new(name: &str, code: &str) -> Self {
        Self {
            name: name.to_string(),
            code: code.to_string(),
            uniforms: HashMap::new(),
            compiled: false,
        }
    }

    pub fn set_uniform(&mut self, name: &str, value: ShaderUniform) {
        self.uniforms.insert(name.to_string(), value);
    }

    pub fn get_uniform(&self, name: &str) -> Option<&ShaderUniform> {
        self.uniforms.get(name)
    }

    pub fn compile(&mut self) -> Result<(), String> {
        // Valida o código do shader
        if self.code.is_empty() {
            return Err("Shader code is empty".to_string());
        }

        // Verifica se tem a função main
        if !self.code.contains("fn main") && !self.code.contains("fn process") {
            return Err("Shader must have a 'main' or 'process' function".to_string());
        }

        self.compiled = true;
        Ok(())
    }

    pub fn apply(&self, pixel: [u8; 4], x: u32, y: u32, width: u32, height: u32) -> [u8; 4] {
        if !self.compiled {
            return pixel;
        }

        // Interpreta o shader (versão simplificada)
        // Em produção, isso seria um interpretador completo
        self.interpret_shader(pixel, x, y, width, height)
    }

    fn interpret_shader(&self, pixel: [u8; 4], x: u32, y: u32, width: u32, height: u32) -> [u8; 4] {
        let mut result = pixel;

        // Normaliza coordenadas
        let u = x as f32 / width as f32;
        let v = y as f32 / height as f32;

        // Interpreta comandos simples do shader
        if self.code.contains("invert") {
            result[0] = 255 - result[0];
            result[1] = 255 - result[1];
            result[2] = 255 - result[2];
        }

        if self.code.contains("grayscale") {
            let gray = (result[0] as f32 * 0.299 + result[1] as f32 * 0.587 + result[2] as f32 * 0.114) as u8;
            result[0] = gray;
            result[1] = gray;
            result[2] = gray;
        }

        if self.code.contains("sepia") {
            let r = result[0] as f32;
            let g = result[1] as f32;
            let b = result[2] as f32;
            
            result[0] = ((r * 0.393 + g * 0.769 + b * 0.189).min(255.0)) as u8;
            result[1] = ((r * 0.349 + g * 0.686 + b * 0.168).min(255.0)) as u8;
            result[2] = ((r * 0.272 + g * 0.534 + b * 0.131).min(255.0)) as u8;
        }

        if self.code.contains("vignette") {
            let dx = u - 0.5;
            let dy = v - 0.5;
            let dist = (dx * dx + dy * dy).sqrt();
            let vignette = (1.0 - dist * 1.5).max(0.0);
            
            result[0] = (result[0] as f32 * vignette) as u8;
            result[1] = (result[1] as f32 * vignette) as u8;
            result[2] = (result[2] as f32 * vignette) as u8;
        }

        if self.code.contains("brightness") {
            if let Some(ShaderUniform::Float(brightness)) = self.get_uniform("brightness") {
                result[0] = (result[0] as f32 * brightness).min(255.0) as u8;
                result[1] = (result[1] as f32 * brightness).min(255.0) as u8;
                result[2] = (result[2] as f32 * brightness).min(255.0) as u8;
            }
        }

        if self.code.contains("contrast") {
            if let Some(ShaderUniform::Float(contrast)) = self.get_uniform("contrast") {
                let factor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 - contrast));
                result[0] = ((factor * (result[0] as f32 - 128.0) + 128.0).clamp(0.0, 255.0)) as u8;
                result[1] = ((factor * (result[1] as f32 - 128.0) + 128.0).clamp(0.0, 255.0)) as u8;
                result[2] = ((factor * (result[2] as f32 - 128.0) + 128.0).clamp(0.0, 255.0)) as u8;
            }
        }

        if self.code.contains("tint") {
            if let Some(ShaderUniform::Color(tint)) = self.get_uniform("tint") {
                result[0] = ((result[0] as f32 * tint[0] as f32 / 255.0).min(255.0)) as u8;
                result[1] = ((result[1] as f32 * tint[1] as f32 / 255.0).min(255.0)) as u8;
                result[2] = ((result[2] as f32 * tint[2] as f32 / 255.0).min(255.0)) as u8;
            }
        }

        result
    }

    // Carrega shader de arquivo
    pub fn from_file(path: &str) -> Result<Self, String> {
        let code = std::fs::read_to_string(path)
            .map_err(|e| format!("Failed to read shader file: {}", e))?;
        
        let name = std::path::Path::new(path)
            .file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("unnamed")
            .to_string();

        let mut shader = Self::new(&name, &code);
        shader.compile()?;
        Ok(shader)
    }

    // Salva shader em arquivo
    pub fn save(&self, path: &str) -> Result<(), String> {
        std::fs::write(path, &self.code)
            .map_err(|e| format!("Failed to save shader: {}", e))
    }
}

// Biblioteca de shaders pré-definidos
pub struct ShaderLibrary {
    shaders: HashMap<String, CustomShader>,
}

impl ShaderLibrary {
    pub fn new() -> Self {
        let mut library = Self {
            shaders: HashMap::new(),
        };
        library.load_builtin_shaders();
        library
    }

    fn load_builtin_shaders(&mut self) {
        // Shader de inversão
        let invert = CustomShader::new("invert", "fn main() { invert }");
        self.add_shader(invert);

        // Shader de grayscale
        let grayscale = CustomShader::new("grayscale", "fn main() { grayscale }");
        self.add_shader(grayscale);

        // Shader de sepia
        let sepia = CustomShader::new("sepia", "fn main() { sepia }");
        self.add_shader(sepia);

        // Shader de vignette
        let vignette = CustomShader::new("vignette", "fn main() { vignette }");
        self.add_shader(vignette);

        // Shader de brightness
        let mut brightness = CustomShader::new("brightness", "fn main() { brightness }");
        brightness.set_uniform("brightness", ShaderUniform::Float(1.5));
        self.add_shader(brightness);

        // Shader de contrast
        let mut contrast = CustomShader::new("contrast", "fn main() { contrast }");
        contrast.set_uniform("contrast", ShaderUniform::Float(50.0));
        self.add_shader(contrast);

        // Shader de tint
        let mut tint = CustomShader::new("tint", "fn main() { tint }");
        tint.set_uniform("tint", ShaderUniform::Color([255, 200, 150, 255]));
        self.add_shader(tint);
    }

    pub fn add_shader(&mut self, mut shader: CustomShader) {
        let _ = shader.compile();
        self.shaders.insert(shader.name.clone(), shader);
    }

    pub fn get_shader(&self, name: &str) -> Option<&CustomShader> {
        self.shaders.get(name)
    }

    pub fn get_shader_mut(&mut self, name: &str) -> Option<&mut CustomShader> {
        self.shaders.get_mut(name)
    }

    pub fn list_shaders(&self) -> Vec<String> {
        self.shaders.keys().cloned().collect()
    }

    pub fn apply_shader(&self, name: &str, pixels: &mut [u8], width: u32, height: u32) {
        if let Some(shader) = self.get_shader(name) {
            for y in 0..height {
                for x in 0..width {
                    let idx = ((y * width + x) * 4) as usize;
                    if idx + 3 < pixels.len() {
                        let pixel = [
                            pixels[idx],
                            pixels[idx + 1],
                            pixels[idx + 2],
                            pixels[idx + 3],
                        ];
                        let result = shader.apply(pixel, x, y, width, height);
                        pixels[idx..idx + 4].copy_from_slice(&result);
                    }
                }
            }
        }
    }
}

// Builder para criar shaders facilmente
pub struct ShaderBuilder {
    name: String,
    operations: Vec<String>,
    uniforms: HashMap<String, ShaderUniform>,
}

impl ShaderBuilder {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            operations: Vec::new(),
            uniforms: HashMap::new(),
        }
    }

    pub fn invert(mut self) -> Self {
        self.operations.push("invert".to_string());
        self
    }

    pub fn grayscale(mut self) -> Self {
        self.operations.push("grayscale".to_string());
        self
    }

    pub fn sepia(mut self) -> Self {
        self.operations.push("sepia".to_string());
        self
    }

    pub fn vignette(mut self) -> Self {
        self.operations.push("vignette".to_string());
        self
    }

    pub fn brightness(mut self, value: f32) -> Self {
        self.operations.push("brightness".to_string());
        self.uniforms.insert("brightness".to_string(), ShaderUniform::Float(value));
        self
    }

    pub fn contrast(mut self, value: f32) -> Self {
        self.operations.push("contrast".to_string());
        self.uniforms.insert("contrast".to_string(), ShaderUniform::Float(value));
        self
    }

    pub fn tint(mut self, color: [u8; 4]) -> Self {
        self.operations.push("tint".to_string());
        self.uniforms.insert("tint".to_string(), ShaderUniform::Color(color));
        self
    }

    pub fn build(self) -> CustomShader {
        let code = format!("fn main() {{ {} }}", self.operations.join(" "));
        let mut shader = CustomShader::new(&self.name, &code);
        shader.uniforms = self.uniforms;
        let _ = shader.compile();
        shader
    }
}