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 Partículas 3D
use crate::mesh3d::Vec3;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Particle3D {
    pub position: Vec3,
    pub velocity: Vec3,
    pub acceleration: Vec3,
    pub color: [u8; 4],
    pub size: f32,
    pub lifetime: f32,
    pub max_lifetime: f32,
    pub rotation: f32,
    pub rotation_speed: f32,
    pub scale: f32,
    pub scale_speed: f32,
    pub color_shift: [f32; 4],
    pub turbulence: f32,
}

impl Particle3D {
    pub fn new(position: Vec3, velocity: Vec3, color: [u8; 4], lifetime: f32) -> Self {
        Self {
            position,
            velocity,
            acceleration: Vec3::new(0.0, 0.0, 0.0),
            color,
            size: 0.1,
            lifetime,
            max_lifetime: lifetime,
            rotation: 0.0,
            rotation_speed: 0.0,
            scale: 1.0,
            scale_speed: 0.0,
            color_shift: [0.0, 0.0, 0.0, 0.0],
            turbulence: 0.0,
        }
    }

    pub fn with_acceleration(mut self, acceleration: Vec3) -> Self {
        self.acceleration = acceleration;
        self
    }

    pub fn with_rotation(mut self, rotation: f32, rotation_speed: f32) -> Self {
        self.rotation = rotation;
        self.rotation_speed = rotation_speed;
        self
    }

    pub fn with_scale(mut self, scale: f32, scale_speed: f32) -> Self {
        self.scale = scale;
        self.scale_speed = scale_speed;
        self
    }

    pub fn with_color_shift(mut self, shift: [f32; 4]) -> Self {
        self.color_shift = shift;
        self
    }

    pub fn with_turbulence(mut self, turbulence: f32) -> Self {
        self.turbulence = turbulence;
        self
    }

    pub fn update(&mut self, dt: f32, gravity: Vec3) {
        // Aplica aceleração
        self.velocity.x += (gravity.x + self.acceleration.x) * dt;
        self.velocity.y += (gravity.y + self.acceleration.y) * dt;
        self.velocity.z += (gravity.z + self.acceleration.z) * dt;

        // Aplica turbulência
        if self.turbulence > 0.0 {
            use rand::Rng;
            let mut rng = rand::thread_rng();
            self.velocity.x += rng.gen_range(-self.turbulence..self.turbulence);
            self.velocity.y += rng.gen_range(-self.turbulence..self.turbulence);
            self.velocity.z += rng.gen_range(-self.turbulence..self.turbulence);
        }

        // Atualiza posição
        self.position.x += self.velocity.x * dt;
        self.position.y += self.velocity.y * dt;
        self.position.z += self.velocity.z * dt;

        // Atualiza rotação
        self.rotation += self.rotation_speed * dt;

        // Atualiza escala
        self.scale += self.scale_speed * dt;
        self.scale = self.scale.max(0.0);

        // Atualiza cor
        let life_ratio = self.lifetime / self.max_lifetime;
        for i in 0..3 {
            let shift = self.color_shift[i] * (1.0 - life_ratio);
            self.color[i] = (self.color[i] as f32 + shift).clamp(0.0, 255.0) as u8;
        }

        self.lifetime -= dt;

        // Fade out
        let alpha = (life_ratio * 255.0) as u8;
        self.color[3] = alpha;
    }

    pub fn is_alive(&self) -> bool {
        self.lifetime > 0.0
    }
}

#[derive(Debug)]
pub struct ParticleSystem3D {
    pub particles: Vec<Particle3D>,
    pub gravity: Vec3,
    pub max_particles: usize,
}

impl ParticleSystem3D {
    pub fn new(max_particles: usize) -> Self {
        Self {
            particles: Vec::new(),
            gravity: Vec3::new(0.0, -9.8, 0.0),
            max_particles,
        }
    }

    pub fn emit(&mut self, position: Vec3, velocity: Vec3, color: [u8; 4], lifetime: f32) {
        if self.particles.len() < self.max_particles {
            self.particles.push(Particle3D::new(position, velocity, color, lifetime));
        }
    }

    pub fn emit_burst(&mut self, position: Vec3, count: usize, speed: f32, color: [u8; 4], lifetime: f32) {
        use std::f32::consts::PI;
        
        for i in 0..count {
            let angle = (i as f32 / count as f32) * 2.0 * PI;
            let elevation = ((i as f32 / count as f32) - 0.5) * PI;
            
            let velocity = Vec3::new(
                angle.cos() * elevation.cos() * speed,
                elevation.sin() * speed,
                angle.sin() * elevation.cos() * speed,
            );

            self.emit(position, velocity, color, lifetime);
        }
    }

    pub fn update(&mut self, dt: f32) {
        for particle in &mut self.particles {
            particle.update(dt, self.gravity);
        }

        self.particles.retain(|p| p.is_alive());
    }

    pub fn clear(&mut self) {
        self.particles.clear();
    }
}

// Presets de efeitos 3D aprimorados
impl ParticleSystem3D {
    pub fn explosion(&mut self, position: Vec3) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        for _ in 0..50 {
            let angle = rng.gen_range(0.0..std::f32::consts::TAU);
            let elevation = rng.gen_range(-std::f32::consts::FRAC_PI_2..std::f32::consts::FRAC_PI_2);
            let speed = rng.gen_range(3.0..8.0);
            let vel = Vec3::new(
                angle.cos() * elevation.cos() * speed,
                elevation.sin() * speed,
                angle.sin() * elevation.cos() * speed,
            );
            let mut particle = Particle3D::new(position, vel, [255, 150, 50, 255], rng.gen_range(0.5..1.5));
            particle.color_shift = [-100.0, -50.0, 0.0, 0.0];
            particle.scale_speed = -0.5;
            self.particles.push(particle);
        }
    }

    pub fn smoke(&mut self, position: Vec3) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        for _ in 0..5 {
            let vel = Vec3::new(
                rng.gen_range(-0.5..0.5),
                rng.gen_range(1.0..3.0),
                rng.gen_range(-0.5..0.5),
            );
            let mut particle = Particle3D::new(position, vel, [100, 100, 100, 200], rng.gen_range(2.0..4.0));
            particle.scale_speed = 0.3;
            particle.turbulence = 0.1;
            self.particles.push(particle);
        }
    }

    pub fn sparkles(&mut self, position: Vec3) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        for _ in 0..10 {
            let vel = Vec3::new(
                rng.gen_range(-3.0..3.0),
                rng.gen_range(0.0..3.0),
                rng.gen_range(-3.0..3.0),
            );
            let mut particle = Particle3D::new(position, vel, [255, 255, 100, 255], rng.gen_range(0.3..0.8));
            particle.rotation_speed = rng.gen_range(-10.0..10.0);
            self.particles.push(particle);
        }
    }

    pub fn fire(&mut self, position: Vec3) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        for _ in 0..8 {
            let vel = Vec3::new(
                rng.gen_range(-0.3..0.3),
                rng.gen_range(2.0..4.0),
                rng.gen_range(-0.3..0.3),
            );
            let color = if rng.gen_bool(0.7) {
                [255, rng.gen_range(100..200), 0, 255]
            } else {
                [255, 255, 0, 255]
            };
            let mut particle = Particle3D::new(position, vel, color, rng.gen_range(0.5..1.5));
            particle.color_shift = [-50.0, -100.0, 0.0, 0.0];
            particle.scale_speed = -0.2;
            particle.turbulence = 0.2;
            self.particles.push(particle);
        }
    }

    pub fn magic(&mut self, position: Vec3) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        for _ in 0..15 {
            let angle = rng.gen_range(0.0..std::f32::consts::TAU);
            let radius = rng.gen_range(0.5..2.0);
            let vel = Vec3::new(
                angle.cos() * radius,
                rng.gen_range(-1.0..2.0),
                angle.sin() * radius,
            );
            let hue = rng.gen_range(0..360);
            let color = Self::hsv_to_rgb(hue as f32, 1.0, 1.0);
            let mut particle = Particle3D::new(position, vel, color, rng.gen_range(0.8..2.0));
            particle.rotation_speed = rng.gen_range(-15.0..15.0);
            particle.turbulence = 0.15;
            self.particles.push(particle);
        }
    }

    pub fn trail(&mut self, position: Vec3, direction: Vec3) {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        for _ in 0..3 {
            let vel = Vec3::new(
                direction.x * 0.5 + rng.gen_range(-0.5..0.5),
                direction.y * 0.5 + rng.gen_range(-0.5..0.5),
                direction.z * 0.5 + rng.gen_range(-0.5..0.5),
            );
            let mut particle = Particle3D::new(position, vel, [200, 200, 255, 255], rng.gen_range(0.3..0.8));
            particle.scale_speed = -0.5;
            self.particles.push(particle);
        }
    }

    fn hsv_to_rgb(h: f32, s: f32, v: f32) -> [u8; 4] {
        let c = v * s;
        let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
        let m = v - c;
        let (r, g, b) = if h < 60.0 {
            (c, x, 0.0)
        } else if h < 120.0 {
            (x, c, 0.0)
        } else if h < 180.0 {
            (0.0, c, x)
        } else if h < 240.0 {
            (0.0, x, c)
        } else if h < 300.0 {
            (x, 0.0, c)
        } else {
            (c, 0.0, x)
        };
        [
            ((r + m) * 255.0) as u8,
            ((g + m) * 255.0) as u8,
            ((b + m) * 255.0) as u8,
            255,
        ]
    }
}