use crate::mesh3d::{Mesh3D, Vec3};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LODMesh {
pub levels: Vec<LODLevel>,
pub current_level: usize,
pub auto_switch: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LODLevel {
pub mesh: Mesh3D,
pub distance: f32,
pub quality: LODQuality,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LODQuality {
Ultra, High, Medium, Low, VeryLow, }
impl LODMesh {
pub fn new(base_mesh: Mesh3D) -> Self {
let levels = vec![
LODLevel {
mesh: base_mesh.clone(),
distance: 0.0,
quality: LODQuality::Ultra,
},
LODLevel {
mesh: Self::simplify_mesh(&base_mesh, 0.75),
distance: 50.0,
quality: LODQuality::High,
},
LODLevel {
mesh: Self::simplify_mesh(&base_mesh, 0.5),
distance: 100.0,
quality: LODQuality::Medium,
},
LODLevel {
mesh: Self::simplify_mesh(&base_mesh, 0.25),
distance: 200.0,
quality: LODQuality::Low,
},
LODLevel {
mesh: Self::simplify_mesh(&base_mesh, 0.1),
distance: 400.0,
quality: LODQuality::VeryLow,
},
];
Self {
levels,
current_level: 0,
auto_switch: true,
}
}
pub fn with_custom_levels(levels: Vec<LODLevel>) -> Self {
Self {
levels,
current_level: 0,
auto_switch: true,
}
}
pub fn update(&mut self, camera_position: Vec3, object_position: Vec3) {
if !self.auto_switch {
return;
}
let distance = camera_position.distance(&object_position);
for (i, level) in self.levels.iter().enumerate().rev() {
if distance >= level.distance {
self.current_level = i;
break;
}
}
}
pub fn get_current_mesh(&self) -> &Mesh3D {
&self.levels[self.current_level].mesh
}
pub fn get_current_quality(&self) -> LODQuality {
self.levels[self.current_level].quality
}
pub fn set_quality(&mut self, quality: LODQuality) {
self.auto_switch = false;
for (i, level) in self.levels.iter().enumerate() {
if level.quality == quality {
self.current_level = i;
break;
}
}
}
pub fn enable_auto_switch(&mut self) {
self.auto_switch = true;
}
fn simplify_mesh(mesh: &Mesh3D, factor: f32) -> Mesh3D {
if factor >= 1.0 {
return mesh.clone();
}
let target_vertices = (mesh.vertices.len() as f32 * factor) as usize;
let target_vertices = target_vertices.max(3);
if target_vertices >= mesh.vertices.len() {
return mesh.clone();
}
let step = mesh.vertices.len() / target_vertices;
let step = step.max(1);
let mut new_vertices = Vec::new();
let mut new_indices = Vec::new();
for i in (0..mesh.vertices.len()).step_by(step) {
new_vertices.push(mesh.vertices[i].clone());
}
for i in (0..new_vertices.len()).step_by(3) {
if i + 2 < new_vertices.len() {
new_indices.push(i as u32);
new_indices.push((i + 1) as u32);
new_indices.push((i + 2) as u32);
}
}
let mut simplified = Mesh3D::finalize(new_vertices, new_indices);
simplified.position = mesh.position;
simplified.rotation = mesh.rotation;
simplified.scale = mesh.scale;
simplified
}
pub fn get_stats(&self) -> LODStats {
LODStats {
current_level: self.current_level,
current_quality: self.get_current_quality(),
vertex_count: self.levels[self.current_level].mesh.vertices.len(),
triangle_count: self.levels[self.current_level].mesh.indices.len() / 3,
total_levels: self.levels.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct LODStats {
pub current_level: usize,
pub current_quality: LODQuality,
pub vertex_count: usize,
pub triangle_count: usize,
pub total_levels: usize,
}
impl LODQuality {
pub fn from_distance(distance: f32) -> Self {
if distance < 50.0 {
LODQuality::Ultra
} else if distance < 100.0 {
LODQuality::High
} else if distance < 200.0 {
LODQuality::Medium
} else if distance < 400.0 {
LODQuality::Low
} else {
LODQuality::VeryLow
}
}
pub fn get_simplification_factor(&self) -> f32 {
match self {
LODQuality::Ultra => 1.0,
LODQuality::High => 0.75,
LODQuality::Medium => 0.5,
LODQuality::Low => 0.25,
LODQuality::VeryLow => 0.1,
}
}
}