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 Frustum Culling para otimização de renderização
use crate::camera3d::Camera3D;
use crate::mesh3d::Vec3;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Frustum {
    pub planes: [Plane; 6],
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Plane {
    pub normal: Vec3,
    pub distance: f32,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct BoundingBox {
    pub min: Vec3,
    pub max: Vec3,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct BoundingSphere {
    pub center: Vec3,
    pub radius: f32,
}

impl Frustum {
    pub fn from_camera(camera: &Camera3D, aspect_ratio: f32) -> Self {
        let fov_rad = camera.fov.to_radians();
        let near = camera.near;
        let far = camera.far;

        // Calcula dimensões do frustum
        let near_height = 2.0 * (fov_rad / 2.0).tan() * near;
        let near_width = near_height * aspect_ratio;
        let far_height = 2.0 * (fov_rad / 2.0).tan() * far;
        let _far_width = far_height * aspect_ratio;

        // Direções da câmera
        let forward = camera.get_forward();
        let right = camera.get_right();
        let up = camera.get_up();

        // Centros dos planos near e far
        let near_center = camera.position + forward * near;
        let far_center = camera.position + forward * far;

        // Planos do frustum
        let planes = [
            // Near
            Plane {
                normal: forward,
                distance: -forward.dot(&near_center),
            },
            // Far
            Plane {
                normal: -forward,
                distance: forward.dot(&far_center),
            },
            // Left
            Plane {
                normal: (forward * near + right * (near_width / 2.0))
                    .cross(&up)
                    .normalize(),
                distance: 0.0,
            },
            // Right
            Plane {
                normal: up
                    .cross(&(forward * near - right * (near_width / 2.0)))
                    .normalize(),
                distance: 0.0,
            },
            // Top
            Plane {
                normal: (forward * near - up * (near_height / 2.0))
                    .cross(&right)
                    .normalize(),
                distance: 0.0,
            },
            // Bottom
            Plane {
                normal: right
                    .cross(&(forward * near + up * (near_height / 2.0)))
                    .normalize(),
                distance: 0.0,
            },
        ];

        Self { planes }
    }

    pub fn contains_point(&self, point: Vec3) -> bool {
        for plane in &self.planes {
            if plane.distance_to_point(point) < 0.0 {
                return false;
            }
        }
        true
    }

    pub fn contains_sphere(&self, sphere: &BoundingSphere) -> bool {
        for plane in &self.planes {
            if plane.distance_to_point(sphere.center) < -sphere.radius {
                return false;
            }
        }
        true
    }

    pub fn contains_box(&self, bbox: &BoundingBox) -> bool {
        for plane in &self.planes {
            let mut inside = false;
            
            // Testa os 8 cantos da bounding box
            for x in &[bbox.min.x, bbox.max.x] {
                for y in &[bbox.min.y, bbox.max.y] {
                    for z in &[bbox.min.z, bbox.max.z] {
                        let point = Vec3::new(*x, *y, *z);
                        if plane.distance_to_point(point) >= 0.0 {
                            inside = true;
                            break;
                        }
                    }
                    if inside {
                        break;
                    }
                }
                if inside {
                    break;
                }
            }
            
            if !inside {
                return false;
            }
        }
        true
    }

    pub fn intersects_sphere(&self, sphere: &BoundingSphere) -> CullingResult {
        let mut intersecting = false;
        
        for plane in &self.planes {
            let distance = plane.distance_to_point(sphere.center);
            
            if distance < -sphere.radius {
                return CullingResult::Outside;
            }
            
            if distance.abs() < sphere.radius {
                intersecting = true;
            }
        }
        
        if intersecting {
            CullingResult::Intersecting
        } else {
            CullingResult::Inside
        }
    }
}

impl Plane {
    pub fn new(normal: Vec3, distance: f32) -> Self {
        Self { normal, distance }
    }

    pub fn distance_to_point(&self, point: Vec3) -> f32 {
        self.normal.dot(&point) + self.distance
    }
}

impl BoundingBox {
    pub fn new(min: Vec3, max: Vec3) -> Self {
        Self { min, max }
    }

    pub fn from_points(points: &[Vec3]) -> Self {
        if points.is_empty() {
            return Self {
                min: Vec3::zero(),
                max: Vec3::zero(),
            };
        }

        let mut min = points[0];
        let mut max = points[0];

        for point in points.iter().skip(1) {
            min.x = min.x.min(point.x);
            min.y = min.y.min(point.y);
            min.z = min.z.min(point.z);
            max.x = max.x.max(point.x);
            max.y = max.y.max(point.y);
            max.z = max.z.max(point.z);
        }

        Self { min, max }
    }

    pub fn center(&self) -> Vec3 {
        Vec3::new(
            (self.min.x + self.max.x) / 2.0,
            (self.min.y + self.max.y) / 2.0,
            (self.min.z + self.max.z) / 2.0,
        )
    }

    pub fn size(&self) -> Vec3 {
        Vec3::new(
            self.max.x - self.min.x,
            self.max.y - self.min.y,
            self.max.z - self.min.z,
        )
    }

    pub fn to_sphere(&self) -> BoundingSphere {
        let center = self.center();
        let radius = center.distance(&self.max);
        BoundingSphere { center, radius }
    }
}

impl BoundingSphere {
    pub fn new(center: Vec3, radius: f32) -> Self {
        Self { center, radius }
    }

    pub fn from_points(points: &[Vec3]) -> Self {
        if points.is_empty() {
            return Self {
                center: Vec3::zero(),
                radius: 0.0,
            };
        }

        // Calcula centro (média dos pontos)
        let mut center = Vec3::zero();
        for point in points {
            center.x += point.x;
            center.y += point.y;
            center.z += point.z;
        }
        center.x /= points.len() as f32;
        center.y /= points.len() as f32;
        center.z /= points.len() as f32;

        // Calcula raio (maior distância do centro)
        let mut radius: f32 = 0.0;
        for point in points {
            let distance = center.distance(point);
            radius = radius.max(distance);
        }

        Self { center, radius }
    }

    pub fn contains_point(&self, point: Vec3) -> bool {
        self.center.distance(&point) <= self.radius
    }

    pub fn intersects(&self, other: &BoundingSphere) -> bool {
        let distance = self.center.distance(&other.center);
        distance <= self.radius + other.radius
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CullingResult {
    Inside,
    Outside,
    Intersecting,
}

pub struct CullingStats {
    pub total_objects: usize,
    pub visible_objects: usize,
    pub culled_objects: usize,
    pub culling_percentage: f32,
}

impl CullingStats {
    pub fn new(total: usize, visible: usize) -> Self {
        let culled = total.saturating_sub(visible);
        let percentage = if total > 0 {
            (culled as f32 / total as f32) * 100.0
        } else {
            0.0
        };

        Self {
            total_objects: total,
            visible_objects: visible,
            culled_objects: culled,
            culling_percentage: percentage,
        }
    }
}