use glam::Vec3;
use crate::{meshes::MeshKey, AwsmRenderer};
#[derive(Clone, Copy, Debug)]
pub struct SkinLodLevel {
pub max_distance: f32,
pub period: u8,
}
pub fn period_for_distance(levels: &[SkinLodLevel], dist: f32) -> u8 {
levels
.iter()
.find(|lvl| dist <= lvl.max_distance)
.map(|lvl| lvl.period)
.unwrap_or_else(|| levels.last().map(|l| l.period).unwrap_or(1))
.max(1)
}
impl AwsmRenderer {
pub fn set_mesh_skin_update_period(
&mut self,
mesh_key: MeshKey,
period: u8,
) -> crate::error::Result<()> {
let mesh = self.meshes.get_mut(mesh_key)?;
mesh.skin_update_period = period.max(1);
Ok(())
}
pub fn set_skin_update_periods_by_distance(
&mut self,
camera_pos: Vec3,
levels: &[SkinLodLevel],
) {
if levels.is_empty() {
return;
}
let snapshot: Vec<(MeshKey, Vec3)> = self
.scene_spatial
.iter_all()
.map(|node| (node.mesh_key, node.aabb.center()))
.collect();
for (mesh_key, center) in snapshot {
let has_skin = self
.meshes
.mesh_skin_key(mesh_key)
.map(|opt| opt.is_some())
.unwrap_or(false);
if !has_skin {
continue;
}
let dist = (center - camera_pos).length();
let period = period_for_distance(levels, dist);
if let Ok(mesh) = self.meshes.get_mut(mesh_key) {
mesh.skin_update_period = period;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn lod_table() -> [SkinLodLevel; 3] {
[
SkinLodLevel {
max_distance: 10.0,
period: 1,
},
SkinLodLevel {
max_distance: 30.0,
period: 2,
},
SkinLodLevel {
max_distance: 80.0,
period: 4,
},
]
}
#[test]
fn lod_levels_pick_first_match() {
let levels = lod_table();
assert_eq!(period_for_distance(&levels, 5.0), 1);
assert_eq!(period_for_distance(&levels, 20.0), 2);
assert_eq!(period_for_distance(&levels, 50.0), 4);
assert_eq!(
period_for_distance(&levels, 200.0),
4,
"past last threshold, sticks at slowest tier"
);
}
#[test]
fn lod_boundary_is_inclusive() {
let levels = lod_table();
assert_eq!(period_for_distance(&levels, 10.0), 1, "edge of tier 0");
assert_eq!(period_for_distance(&levels, 30.0), 2, "edge of tier 1");
assert_eq!(period_for_distance(&levels, 80.0), 4, "edge of tier 2");
}
#[test]
fn lod_empty_table_is_every_frame() {
assert_eq!(
period_for_distance(&[], 42.0),
1,
"no table → update every frame"
);
}
#[test]
fn lod_period_floored_at_one() {
let levels = [SkinLodLevel {
max_distance: 100.0,
period: 0,
}];
assert_eq!(period_for_distance(&levels, 5.0), 1);
assert_eq!(
period_for_distance(&levels, 500.0),
1,
"past-last fallback is also floored"
);
}
}