#![allow(missing_docs)]
#![allow(dead_code)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LodTier {
Full,
Reduced,
Minimal,
Frozen,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LodConfig {
pub full_radius: f64,
pub reduced_radius: f64,
pub minimal_radius: f64,
pub substeps_full: u32,
pub substeps_reduced: u32,
pub substeps_minimal: u32,
}
impl Default for LodConfig {
fn default() -> Self {
Self {
full_radius: 50.0,
reduced_radius: 150.0,
minimal_radius: 300.0,
substeps_full: 4,
substeps_reduced: 2,
substeps_minimal: 1,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LodBody {
pub id: u32,
pub position: [f64; 3],
pub tier: LodTier,
pub priority: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LodUpdate {
pub id: u32,
pub old_tier: LodTier,
pub new_tier: LodTier,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LodSystem {
pub config: LodConfig,
bodies: Vec<LodBody>,
next_id: u32,
pub focus: [f64; 3],
}
impl LodSystem {
pub fn new(config: LodConfig, focus: [f64; 3]) -> Self {
Self {
config,
bodies: Vec::new(),
next_id: 0,
focus,
}
}
pub fn add_body(&mut self, position: [f64; 3], priority: f32) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.bodies.push(LodBody {
id,
position,
tier: LodTier::Full,
priority: priority.clamp(0.0, 1.0),
});
id
}
pub fn remove_body(&mut self, id: u32) {
self.bodies.retain(|b| b.id != id);
}
pub fn body(&self, id: u32) -> Option<&LodBody> {
self.bodies.iter().find(|b| b.id == id)
}
pub fn update_position(&mut self, id: u32, position: [f64; 3]) {
if let Some(b) = self.bodies.iter_mut().find(|b| b.id == id) {
b.position = position;
}
}
pub fn set_priority(&mut self, id: u32, priority: f32) {
if let Some(b) = self.bodies.iter_mut().find(|b| b.id == id) {
b.priority = priority.clamp(0.0, 1.0);
}
}
pub fn set_focus(&mut self, focus: [f64; 3]) {
self.focus = focus;
}
fn compute_tier(&self, dist: f64, priority: f32) -> LodTier {
let effective = dist * (1.0 - (priority as f64) * 0.3);
let cfg = &self.config;
if effective <= cfg.full_radius {
LodTier::Full
} else if effective <= cfg.reduced_radius {
LodTier::Reduced
} else if effective <= cfg.minimal_radius {
LodTier::Minimal
} else {
LodTier::Frozen
}
}
pub fn update(&mut self) -> Vec<LodUpdate> {
let focus = self.focus;
let mut transitions = Vec::new();
for body in &mut self.bodies {
let dx = body.position[0] - focus[0];
let dy = body.position[1] - focus[1];
let dz = body.position[2] - focus[2];
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
let new_tier = {
let effective = dist * (1.0 - (body.priority as f64) * 0.3);
let cfg = &self.config;
if effective <= cfg.full_radius {
LodTier::Full
} else if effective <= cfg.reduced_radius {
LodTier::Reduced
} else if effective <= cfg.minimal_radius {
LodTier::Minimal
} else {
LodTier::Frozen
}
};
if body.tier != new_tier {
transitions.push(LodUpdate {
id: body.id,
old_tier: body.tier,
new_tier,
});
body.tier = new_tier;
}
}
transitions
}
pub fn tier(&self, id: u32) -> Option<LodTier> {
self.bodies.iter().find(|b| b.id == id).map(|b| b.tier)
}
pub fn substeps_for(&self, id: u32) -> u32 {
match self.tier(id) {
Some(LodTier::Full) => self.config.substeps_full,
Some(LodTier::Reduced) => self.config.substeps_reduced,
Some(LodTier::Minimal) => self.config.substeps_minimal,
Some(LodTier::Frozen) | None => 0,
}
}
pub fn bodies_in_tier(&self, tier: LodTier) -> Vec<u32> {
self.bodies
.iter()
.filter(|b| b.tier == tier)
.map(|b| b.id)
.collect()
}
pub fn body_count(&self) -> usize {
self.bodies.len()
}
pub fn bodies(&self) -> impl Iterator<Item = &LodBody> {
self.bodies.iter()
}
pub fn distance_to_focus(&self, id: u32) -> Option<f64> {
self.bodies.iter().find(|b| b.id == id).map(|b| {
let dx = b.position[0] - self.focus[0];
let dy = b.position[1] - self.focus[1];
let dz = b.position[2] - self.focus[2];
(dx * dx + dy * dy + dz * dz).sqrt()
})
}
pub fn tier_counts(&self) -> (usize, usize, usize, usize) {
let mut counts = (0usize, 0usize, 0usize, 0usize);
for b in &self.bodies {
match b.tier {
LodTier::Full => counts.0 += 1,
LodTier::Reduced => counts.1 += 1,
LodTier::Minimal => counts.2 += 1,
LodTier::Frozen => counts.3 += 1,
}
}
counts
}
pub fn would_transition(&self, id: u32) -> bool {
if let Some(body) = self.bodies.iter().find(|b| b.id == id) {
let dx = body.position[0] - self.focus[0];
let dy = body.position[1] - self.focus[1];
let dz = body.position[2] - self.focus[2];
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
self.compute_tier(dist, body.priority) != body.tier
} else {
false
}
}
}