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
// Renderer 3D Experimental (Software Rasterizer)
use crate::camera3d::Camera3D;
use crate::mesh3d::{Mesh3D, Vec3};
use crate::shader3d::PhongShader;
use crate::particles3d::ParticleSystem3D;

pub struct Renderer3D {
    pub camera: Camera3D,
    pub wireframe: bool,
    pub backface_culling: bool,
    pub use_lighting: bool,
    pub shader: Option<PhongShader>,
    pub zbuffer: Vec<f32>,
    pub width: u32,
    pub height: u32,
    pub fog_enabled: bool,
    pub fog_start: f32,
    pub fog_end: f32,
    pub fog_color: [u8; 4],
}

impl Renderer3D {
    pub fn new(width: u32, height: u32) -> Self {
        let zbuffer_size = (width * height) as usize;
        Self {
            camera: Camera3D::new(width, height),
            wireframe: false,
            backface_culling: true,
            use_lighting: false,
            shader: None,
            zbuffer: vec![f32::INFINITY; zbuffer_size],
            width,
            height,
            fog_enabled: true,
            fog_start: 10.0,
            fog_end: 50.0,
            fog_color: [100, 120, 150, 255],
        }
    }

    pub fn with_fog(mut self, enabled: bool, start: f32, end: f32, color: [u8; 4]) -> Self {
        self.fog_enabled = enabled;
        self.fog_start = start;
        self.fog_end = end;
        self.fog_color = color;
        self
    }

    pub fn with_lighting(mut self, shader: PhongShader) -> Self {
        self.use_lighting = true;
        self.shader = Some(shader);
        self
    }

    pub fn clear_zbuffer(&mut self) {
        for z in &mut self.zbuffer {
            *z = f32::INFINITY;
        }
    }

    pub fn render_particles(&self, particles: &ParticleSystem3D, frame: &mut [u8], width: u32, height: u32) {
        for particle in &particles.particles {
            if let Some((sx, sy, depth)) = self.camera.project(&particle.position, width, height) {
                let size = (particle.size * 100.0 / depth) as i32;
                
                for dy in -size..=size {
                    for dx in -size..=size {
                        if dx * dx + dy * dy <= size * size {
                            let x = sx + dx;
                            let y = sy + dy;
                            
                            if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
                                let idx = ((y as u32 * width + x as u32) * 4) as usize;
                                if idx + 3 < frame.len() {
                                    // Alpha blending
                                    let alpha = particle.color[3] as f32 / 255.0;
                                    frame[idx] = ((1.0 - alpha) * frame[idx] as f32 + alpha * particle.color[0] as f32) as u8;
                                    frame[idx + 1] = ((1.0 - alpha) * frame[idx + 1] as f32 + alpha * particle.color[1] as f32) as u8;
                                    frame[idx + 2] = ((1.0 - alpha) * frame[idx + 2] as f32 + alpha * particle.color[2] as f32) as u8;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    pub fn render_mesh(&self, mesh: &Mesh3D, frame: &mut [u8], width: u32, height: u32) {
        // Renderiza cada triângulo
        for i in (0..mesh.indices.len()).step_by(3) {
            let idx0 = mesh.indices[i] as usize;
            let idx1 = mesh.indices[i + 1] as usize;
            let idx2 = mesh.indices[i + 2] as usize;

            if idx0 >= mesh.vertices.len() || idx1 >= mesh.vertices.len() || idx2 >= mesh.vertices.len() {
                continue;
            }

            let v0 = &mesh.vertices[idx0];
            let v1 = &mesh.vertices[idx1];
            let v2 = &mesh.vertices[idx2];

            // Transforma vértices
            let p0 = self.transform_vertex(&v0.position, mesh);
            let p1 = self.transform_vertex(&v1.position, mesh);
            let p2 = self.transform_vertex(&v2.position, mesh);

            // Backface culling melhorado
            if self.backface_culling {
                let edge1 = Vec3::new(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z);
                let edge2 = Vec3::new(p2.x - p0.x, p2.y - p0.y, p2.z - p0.z);
                let normal = edge1.cross(&edge2);
                
                // Vetor da câmera para o triângulo
                let to_camera = Vec3::new(
                    self.camera.position.x - p0.x,
                    self.camera.position.y - p0.y,
                    self.camera.position.z - p0.z,
                );

                // Se o normal aponta para longe da câmera, não renderiza
                if normal.dot(&to_camera) < 0.0 {
                    continue;
                }
            }

            // Projeta para 2D
            if let (Some(s0), Some(s1), Some(s2)) = (
                self.camera.project(&p0, width, height),
                self.camera.project(&p1, width, height),
                self.camera.project(&p2, width, height),
            ) {
                // Calcula cor com base na distância (fog simples)
                let avg_depth = (s0.2 + s1.2 + s2.2) / 3.0;
                let fog_factor = (1.0 - (avg_depth / 50.0).min(1.0)).max(0.0);
                let color = [
                    (v0.color[0] as f32 * fog_factor) as u8,
                    (v0.color[1] as f32 * fog_factor) as u8,
                    (v0.color[2] as f32 * fog_factor) as u8,
                    v0.color[3],
                ];
                
                if self.wireframe {
                    self.draw_line(frame, width, height, s0.0, s0.1, s1.0, s1.1, color);
                    self.draw_line(frame, width, height, s1.0, s1.1, s2.0, s2.1, color);
                    self.draw_line(frame, width, height, s2.0, s2.1, s0.0, s0.1, color);
                } else {
                    self.draw_triangle(frame, width, height, s0, s1, s2, color);
                }
            }
        }
    }

    fn transform_vertex(&self, vertex: &Vec3, mesh: &Mesh3D) -> Vec3 {
        // Aplica escala
        let mut v = Vec3::new(
            vertex.x * mesh.scale.x,
            vertex.y * mesh.scale.y,
            vertex.z * mesh.scale.z,
        );

        // Aplica rotação (simplificada)
        let cos_x = mesh.rotation.x.cos();
        let sin_x = mesh.rotation.x.sin();
        let cos_y = mesh.rotation.y.cos();
        let sin_y = mesh.rotation.y.sin();
        let cos_z = mesh.rotation.z.cos();
        let sin_z = mesh.rotation.z.sin();

        // Rotação Y
        let temp_x = v.x * cos_y + v.z * sin_y;
        let temp_z = -v.x * sin_y + v.z * cos_y;
        v.x = temp_x;
        v.z = temp_z;

        // Rotação X
        let temp_y = v.y * cos_x - v.z * sin_x;
        let temp_z = v.y * sin_x + v.z * cos_x;
        v.y = temp_y;
        v.z = temp_z;

        // Rotação Z
        let temp_x = v.x * cos_z - v.y * sin_z;
        let temp_y = v.x * sin_z + v.y * cos_z;
        v.x = temp_x;
        v.y = temp_y;

        // Aplica translação
        v.x += mesh.position.x;
        v.y += mesh.position.y;
        v.z += mesh.position.z;

        v
    }

    fn draw_line(&self, frame: &mut [u8], width: u32, height: u32, x0: i32, y0: i32, x1: i32, y1: i32, color: [u8; 4]) {
        let dx = (x1 - x0).abs();
        let dy = (y1 - y0).abs();
        let sx = if x0 < x1 { 1 } else { -1 };
        let sy = if y0 < y1 { 1 } else { -1 };
        let mut err = dx - dy;
        let mut x = x0;
        let mut y = y0;

        loop {
            if x >= 0 && x < width as i32 && y >= 0 && y < height as i32 {
                let idx = ((y as u32 * width + x as u32) * 4) as usize;
                if idx + 3 < frame.len() {
                    frame[idx] = color[0];
                    frame[idx + 1] = color[1];
                    frame[idx + 2] = color[2];
                    frame[idx + 3] = color[3];
                }
            }

            if x == x1 && y == y1 {
                break;
            }

            let e2 = 2 * err;
            if e2 > -dy {
                err -= dy;
                x += sx;
            }
            if e2 < dx {
                err += dx;
                y += sy;
            }
        }
    }

    fn draw_triangle(&self, frame: &mut [u8], width: u32, height: u32, p0: (i32, i32, f32), p1: (i32, i32, f32), p2: (i32, i32, f32), color: [u8; 4]) {
        // Rasterização otimizada de triângulo
        let min_x = p0.0.min(p1.0).min(p2.0).max(0);
        let max_x = p0.0.max(p1.0).max(p2.0).min(width as i32 - 1);
        let min_y = p0.1.min(p1.1).min(p2.1).max(0);
        let max_y = p0.1.max(p1.1).max(p2.1).min(height as i32 - 1);

        // Early exit se fora da tela
        if min_x >= width as i32 || max_x < 0 || min_y >= height as i32 || max_y < 0 {
            return;
        }

        for y in min_y..=max_y {
            for x in min_x..=max_x {
                if self.point_in_triangle(x, y, p0, p1, p2) {
                    let idx = ((y as u32 * width + x as u32) * 4) as usize;
                    if idx + 3 < frame.len() {
                        // Alpha blending simples
                        let alpha = color[3] as f32 / 255.0;
                        if alpha >= 0.99 {
                            frame[idx] = color[0];
                            frame[idx + 1] = color[1];
                            frame[idx + 2] = color[2];
                            frame[idx + 3] = 255;
                        } else {
                            frame[idx] = ((color[0] as f32 * alpha) + (frame[idx] as f32 * (1.0 - alpha))) as u8;
                            frame[idx + 1] = ((color[1] as f32 * alpha) + (frame[idx + 1] as f32 * (1.0 - alpha))) as u8;
                            frame[idx + 2] = ((color[2] as f32 * alpha) + (frame[idx + 2] as f32 * (1.0 - alpha))) as u8;
                        }
                    }
                }
            }
        }
    }

    fn point_in_triangle(&self, px: i32, py: i32, p0: (i32, i32, f32), p1: (i32, i32, f32), p2: (i32, i32, f32)) -> bool {
        let sign = |p1: (i32, i32), p2: (i32, i32), p3: (i32, i32)| -> i32 {
            (p1.0 - p3.0) * (p2.1 - p3.1) - (p2.0 - p3.0) * (p1.1 - p3.1)
        };

        let d1 = sign((px, py), (p0.0, p0.1), (p1.0, p1.1));
        let d2 = sign((px, py), (p1.0, p1.1), (p2.0, p2.1));
        let d3 = sign((px, py), (p2.0, p2.1), (p0.0, p0.1));

        let has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
        let has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);

        !(has_neg && has_pos)
    }
}