#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ShadowCascade {
pub near: f32,
pub far: f32,
pub bias: f32,
pub view_proj: [f32; 16],
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CascadedShadow {
pub cascades: Vec<ShadowCascade>,
pub lambda: f32,
}
#[allow(dead_code)]
pub fn new_cascaded_shadow(n: usize, lambda: f32) -> CascadedShadow {
CascadedShadow { cascades: Vec::with_capacity(n), lambda }
}
#[allow(dead_code)]
pub fn cascade_count(cs: &CascadedShadow) -> usize {
cs.cascades.len()
}
#[allow(dead_code)]
pub fn cascade_split_at(cs: &CascadedShadow, i: usize) -> f32 {
cs.cascades.get(i).map(|c| c.far).unwrap_or(0.0)
}
#[allow(dead_code)]
pub fn cascade_near(cs: &CascadedShadow, i: usize) -> f32 {
cs.cascades.get(i).map(|c| c.near).unwrap_or(0.0)
}
#[allow(dead_code)]
pub fn cascade_far(cs: &CascadedShadow, i: usize) -> f32 {
cs.cascades.get(i).map(|c| c.far).unwrap_or(0.0)
}
#[allow(dead_code)]
pub fn compute_cascade_splits(cs: &mut CascadedShadow, near: f32, far: f32, n: usize) {
cs.cascades.clear();
let lambda = cs.lambda;
let mut prev_near = near;
for i in 0..n {
let fi = (i + 1) as f32 / n as f32;
let log_split = near * (far / near.max(f32::EPSILON)).powf(fi);
let lin_split = near + (far - near) * fi;
let split = lambda * log_split + (1.0 - lambda) * lin_split;
let identity = [
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
];
cs.cascades.push(ShadowCascade { near: prev_near, far: split, bias: 0.005, view_proj: identity });
prev_near = split;
}
}
#[allow(dead_code)]
pub fn cascade_view_proj(cs: &CascadedShadow, i: usize) -> Option<[f32; 16]> {
cs.cascades.get(i).map(|c| c.view_proj)
}
#[allow(dead_code)]
pub fn cascade_bias(cs: &CascadedShadow, i: usize) -> f32 {
cs.cascades.get(i).map(|c| c.bias).unwrap_or(0.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_cascaded_shadow_empty() {
let cs = new_cascaded_shadow(4, 0.5);
assert_eq!(cascade_count(&cs), 0);
}
#[test]
fn test_compute_cascade_splits_count() {
let mut cs = new_cascaded_shadow(4, 0.5);
compute_cascade_splits(&mut cs, 0.1, 100.0, 4);
assert_eq!(cascade_count(&cs), 4);
}
#[test]
fn test_cascade_splits_increasing() {
let mut cs = new_cascaded_shadow(4, 0.5);
compute_cascade_splits(&mut cs, 0.1, 100.0, 4);
for i in 1..cascade_count(&cs) {
assert!(cascade_far(&cs, i) > cascade_far(&cs, i - 1));
}
}
#[test]
fn test_cascade_near_first() {
let mut cs = new_cascaded_shadow(3, 0.5);
compute_cascade_splits(&mut cs, 0.1, 50.0, 3);
assert!((cascade_near(&cs, 0) - 0.1).abs() < 1e-5);
}
#[test]
fn test_cascade_split_at_last() {
let mut cs = new_cascaded_shadow(3, 0.5);
compute_cascade_splits(&mut cs, 0.1, 50.0, 3);
let last = cascade_count(&cs) - 1;
assert!(cascade_split_at(&cs, last) <= 50.0 + 1e-3);
}
#[test]
fn test_cascade_view_proj_identity() {
let mut cs = new_cascaded_shadow(2, 0.5);
compute_cascade_splits(&mut cs, 0.1, 10.0, 2);
let vp = cascade_view_proj(&cs, 0).expect("should succeed");
assert!((vp[0] - 1.0).abs() < 1e-6);
}
#[test]
fn test_cascade_bias_default() {
let mut cs = new_cascaded_shadow(1, 0.5);
compute_cascade_splits(&mut cs, 0.1, 10.0, 1);
assert!((cascade_bias(&cs, 0) - 0.005).abs() < 1e-6);
}
#[test]
fn test_cascade_out_of_bounds() {
let cs = new_cascaded_shadow(4, 0.5);
assert_eq!(cascade_far(&cs, 99), 0.0);
assert!(cascade_view_proj(&cs, 99).is_none());
}
}