#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LodConfig {
pub enabled: bool,
pub max_distance: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LodLevel {
pub distance_threshold: f32,
pub vertex_count: u32,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LodSwitcher {
pub config: LodConfig,
pub levels: Vec<LodLevel>,
pub enabled: bool,
}
#[allow(dead_code)]
pub fn default_lod_config() -> LodConfig {
LodConfig {
enabled: true,
max_distance: 1000.0,
}
}
#[allow(dead_code)]
pub fn new_lod_switcher(cfg: &LodConfig) -> LodSwitcher {
LodSwitcher {
enabled: cfg.enabled,
config: cfg.clone(),
levels: Vec::new(),
}
}
#[allow(dead_code)]
pub fn lod_add_level(switcher: &mut LodSwitcher, distance_threshold: f32, vertex_count: u32) {
switcher.levels.push(LodLevel {
distance_threshold,
vertex_count,
});
switcher
.levels
.sort_by(|a, b| a.distance_threshold.partial_cmp(&b.distance_threshold).unwrap_or(std::cmp::Ordering::Equal));
}
#[allow(dead_code)]
pub fn lod_select_level(switcher: &LodSwitcher, camera_distance: f32) -> usize {
if switcher.levels.is_empty() {
return 0;
}
for (i, level) in switcher.levels.iter().enumerate() {
if camera_distance <= level.distance_threshold {
return i;
}
}
switcher.levels.len() - 1
}
#[allow(dead_code)]
pub fn lod_level_count(switcher: &LodSwitcher) -> usize {
switcher.levels.len()
}
#[allow(dead_code)]
pub fn lod_vertex_count_at(switcher: &LodSwitcher, level: usize) -> u32 {
switcher.levels.get(level).map_or(0, |l| l.vertex_count)
}
#[allow(dead_code)]
pub fn lod_distance_threshold(switcher: &LodSwitcher, level: usize) -> f32 {
switcher
.levels
.get(level)
.map_or(f32::MAX, |l| l.distance_threshold)
}
#[allow(dead_code)]
pub fn lod_is_enabled(switcher: &LodSwitcher) -> bool {
switcher.enabled
}
#[allow(dead_code)]
pub fn set_lod_enabled(switcher: &mut LodSwitcher, enabled: bool) {
switcher.enabled = enabled;
}
#[allow(dead_code)]
pub fn lod_reduction_ratio(switcher: &LodSwitcher, level: usize) -> f32 {
if switcher.levels.is_empty() {
return 1.0;
}
let base = switcher.levels[0].vertex_count;
if base == 0 {
return 1.0;
}
let target = lod_vertex_count_at(switcher, level);
if target == 0 {
return 1.0;
}
target as f32 / base as f32
}
#[cfg(test)]
mod tests {
use super::*;
fn make_switcher() -> LodSwitcher {
let cfg = default_lod_config();
let mut sw = new_lod_switcher(&cfg);
lod_add_level(&mut sw, 10.0, 10_000);
lod_add_level(&mut sw, 50.0, 5_000);
lod_add_level(&mut sw, 200.0, 1_000);
sw
}
#[test]
fn test_default_config() {
let cfg = default_lod_config();
assert!(cfg.enabled);
assert!((cfg.max_distance - 1000.0).abs() < 1e-5);
}
#[test]
fn test_new_switcher_empty() {
let cfg = default_lod_config();
let sw = new_lod_switcher(&cfg);
assert_eq!(lod_level_count(&sw), 0);
assert!(lod_is_enabled(&sw));
}
#[test]
fn test_add_level_sorted() {
let cfg = default_lod_config();
let mut sw = new_lod_switcher(&cfg);
lod_add_level(&mut sw, 50.0, 5_000);
lod_add_level(&mut sw, 10.0, 10_000);
lod_add_level(&mut sw, 200.0, 1_000);
assert_eq!(lod_level_count(&sw), 3);
assert!((lod_distance_threshold(&sw, 0) - 10.0).abs() < 1e-5);
assert!((lod_distance_threshold(&sw, 1) - 50.0).abs() < 1e-5);
assert!((lod_distance_threshold(&sw, 2) - 200.0).abs() < 1e-5);
}
#[test]
fn test_select_level_close() {
let sw = make_switcher();
assert_eq!(lod_select_level(&sw, 5.0), 0);
}
#[test]
fn test_select_level_mid() {
let sw = make_switcher();
assert_eq!(lod_select_level(&sw, 25.0), 1);
}
#[test]
fn test_select_level_far() {
let sw = make_switcher();
assert_eq!(lod_select_level(&sw, 500.0), 2);
}
#[test]
fn test_vertex_count() {
let sw = make_switcher();
assert_eq!(lod_vertex_count_at(&sw, 0), 10_000);
assert_eq!(lod_vertex_count_at(&sw, 2), 1_000);
assert_eq!(lod_vertex_count_at(&sw, 99), 0);
}
#[test]
fn test_enable_disable() {
let cfg = default_lod_config();
let mut sw = new_lod_switcher(&cfg);
set_lod_enabled(&mut sw, false);
assert!(!lod_is_enabled(&sw));
set_lod_enabled(&mut sw, true);
assert!(lod_is_enabled(&sw));
}
#[test]
fn test_reduction_ratio() {
let sw = make_switcher();
let r0 = lod_reduction_ratio(&sw, 0);
assert!((r0 - 1.0).abs() < 1e-5);
let r2 = lod_reduction_ratio(&sw, 2);
assert!((r2 - 0.1).abs() < 1e-5);
}
#[test]
fn test_select_empty_switcher() {
let cfg = default_lod_config();
let sw = new_lod_switcher(&cfg);
assert_eq!(lod_select_level(&sw, 100.0), 0);
}
}