#![allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct CameraParams {
pub position: [f32; 3],
pub fov_radians: f32,
pub screen_height_px: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct BoundingSphere {
pub center: [f32; 3],
pub radius: f32,
}
#[derive(Debug, Clone, Copy)]
pub struct LodQuery {
pub screen_coverage: f32,
pub recommended_level: u32,
pub distance: f32,
}
pub fn camera_distance(cam: &CameraParams, sphere: &BoundingSphere) -> f32 {
let dx = cam.position[0] - sphere.center[0];
let dy = cam.position[1] - sphere.center[1];
let dz = cam.position[2] - sphere.center[2];
(dx * dx + dy * dy + dz * dz).sqrt().max(f32::EPSILON)
}
pub fn screen_coverage(cam: &CameraParams, sphere: &BoundingSphere) -> f32 {
let dist = camera_distance(cam, sphere);
let projected_radius = sphere.radius / (dist * (cam.fov_radians * 0.5).tan());
(projected_radius * cam.screen_height_px as f32).clamp(0.0, 1.0)
}
pub fn select_lod_level(coverage: f32, max_levels: u32) -> u32 {
let coverage = coverage.clamp(0.0, 1.0);
let inv = 1.0 - coverage;
((inv * max_levels as f32) as u32).min(max_levels.saturating_sub(1))
}
pub fn query_lod(cam: &CameraParams, sphere: &BoundingSphere, max_levels: u32) -> LodQuery {
let distance = camera_distance(cam, sphere);
let coverage = screen_coverage(cam, sphere);
let recommended_level = select_lod_level(coverage, max_levels);
LodQuery {
screen_coverage: coverage,
recommended_level,
distance,
}
}
pub fn within_distance_budget(cam: &CameraParams, sphere: &BoundingSphere, max_dist: f32) -> bool {
camera_distance(cam, sphere) <= max_dist
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::FRAC_PI_4;
fn default_cam() -> CameraParams {
CameraParams {
position: [0.0, 0.0, 10.0],
fov_radians: FRAC_PI_4,
screen_height_px: 1080,
}
}
fn default_sphere() -> BoundingSphere {
BoundingSphere {
center: [0.0, 0.0, 0.0],
radius: 1.0,
}
}
#[test]
fn test_camera_distance_basic() {
let d = camera_distance(&default_cam(), &default_sphere());
assert!((d - 10.0).abs() < 0.01);
}
#[test]
fn test_screen_coverage_positive() {
let cov = screen_coverage(&default_cam(), &default_sphere());
assert!(cov > 0.0);
}
#[test]
fn test_select_lod_zero_coverage() {
let level = select_lod_level(0.0, 4);
assert_eq!(level, 3);
}
#[test]
fn test_select_lod_full_coverage() {
let level = select_lod_level(1.0, 4);
assert_eq!(level, 0);
}
#[test]
fn test_select_lod_clamps() {
let level = select_lod_level(-5.0, 3);
assert!(level < 3);
}
#[test]
fn test_query_lod_returns_valid_level() {
let q = query_lod(&default_cam(), &default_sphere(), 5);
assert!(q.recommended_level < 5);
}
#[test]
fn test_within_distance_budget_true() {
assert!(within_distance_budget(
&default_cam(),
&default_sphere(),
20.0
));
}
#[test]
fn test_within_distance_budget_false() {
assert!(!within_distance_budget(
&default_cam(),
&default_sphere(),
5.0
));
}
#[test]
fn test_query_lod_distance_positive() {
let q = query_lod(&default_cam(), &default_sphere(), 4);
assert!(q.distance > 0.0);
}
#[test]
fn test_coverage_clamps_to_one() {
let big_sphere = BoundingSphere {
center: [0.0, 0.0, 9.99],
radius: 100.0,
};
let cov = screen_coverage(&default_cam(), &big_sphere);
assert!((0.0..=1.0).contains(&cov));
}
}