oxihuman_viewer/
shadow_debug.rs1#![allow(dead_code)]
3
4#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct ShadowDebugConfig {
10 pub cascade_count: u32,
12 pub active_cascade: u32,
14 pub show_splits: bool,
16 pub cascade_colors: [[f32; 4]; 4],
18 pub bias_scale: f32,
20 pub show_texels: bool,
22 pub overlay_opacity: f32,
24}
25
26impl Default for ShadowDebugConfig {
27 fn default() -> Self {
28 Self {
29 cascade_count: 4,
30 active_cascade: 0,
31 show_splits: true,
32 cascade_colors: [
33 [1.0, 0.3, 0.3, 0.4],
34 [0.3, 1.0, 0.3, 0.4],
35 [0.3, 0.3, 1.0, 0.4],
36 [1.0, 1.0, 0.3, 0.4],
37 ],
38 bias_scale: 1.0,
39 show_texels: false,
40 overlay_opacity: 0.4,
41 }
42 }
43}
44
45#[allow(dead_code)]
47pub fn new_shadow_debug_config() -> ShadowDebugConfig {
48 ShadowDebugConfig::default()
49}
50
51#[allow(dead_code)]
53pub fn sd_set_active_cascade(cfg: &mut ShadowDebugConfig, idx: u32) {
54 cfg.active_cascade = idx.min(cfg.cascade_count.saturating_sub(1));
55}
56
57#[allow(dead_code)]
59pub fn sd_set_opacity(cfg: &mut ShadowDebugConfig, opacity: f32) {
60 cfg.overlay_opacity = opacity.clamp(0.0, 1.0);
61}
62
63#[allow(dead_code)]
65pub fn sd_toggle_splits(cfg: &mut ShadowDebugConfig) {
66 cfg.show_splits = !cfg.show_splits;
67}
68
69#[allow(dead_code)]
71pub fn sd_active_cascade_color(cfg: &ShadowDebugConfig) -> [f32; 4] {
72 let idx = (cfg.active_cascade as usize).min(3);
73 cfg.cascade_colors[idx]
74}
75
76#[allow(dead_code)]
78pub fn sd_cascade_split(near: f32, far: f32, cascade: u32, total: u32) -> f32 {
79 if total == 0 {
80 return far;
81 }
82 let ratio = far / near.max(0.001);
83 let lambda = 0.7f32;
84 let t = cascade as f32 / total as f32;
85 let log_split = near * ratio.powf(t);
86 let lin_split = near + (far - near) * t;
87 lambda * log_split + (1.0 - lambda) * lin_split
88}
89
90#[allow(dead_code)]
92pub fn shadow_debug_to_json(cfg: &ShadowDebugConfig) -> String {
93 format!(
94 r#"{{"cascade_count":{},"active_cascade":{},"show_splits":{},"overlay_opacity":{:.4}}}"#,
95 cfg.cascade_count, cfg.active_cascade, cfg.show_splits, cfg.overlay_opacity
96 )
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_default() {
105 let c = ShadowDebugConfig::default();
106 assert_eq!(c.cascade_count, 4);
107 }
108
109 #[test]
110 fn test_set_active_cascade_clamp() {
111 let mut c = ShadowDebugConfig::default();
112 sd_set_active_cascade(&mut c, 99);
113 assert!(c.active_cascade < c.cascade_count);
114 }
115
116 #[test]
117 fn test_set_opacity_clamp() {
118 let mut c = ShadowDebugConfig::default();
119 sd_set_opacity(&mut c, 5.0);
120 assert!((c.overlay_opacity - 1.0).abs() < 1e-6);
121 }
122
123 #[test]
124 fn test_toggle_splits() {
125 let mut c = ShadowDebugConfig::default();
126 sd_toggle_splits(&mut c);
127 assert!(!c.show_splits);
128 sd_toggle_splits(&mut c);
129 assert!(c.show_splits);
130 }
131
132 #[test]
133 fn test_active_cascade_color() {
134 let c = ShadowDebugConfig::default();
135 let col = sd_active_cascade_color(&c);
136 assert!((col[0] - 1.0).abs() < 1e-6);
137 }
138
139 #[test]
140 fn test_cascade_split_ordered() {
141 let s0 = sd_cascade_split(0.1, 100.0, 0, 4);
142 let s1 = sd_cascade_split(0.1, 100.0, 1, 4);
143 let s2 = sd_cascade_split(0.1, 100.0, 2, 4);
144 assert!(s0 < s1);
145 assert!(s1 < s2);
146 }
147
148 #[test]
149 fn test_cascade_split_zero_total() {
150 let s = sd_cascade_split(0.1, 100.0, 0, 0);
151 assert!((s - 100.0).abs() < 1e-5);
152 }
153
154 #[test]
155 fn test_to_json() {
156 let j = shadow_debug_to_json(&ShadowDebugConfig::default());
157 assert!(j.contains("cascade_count"));
158 assert!(j.contains("show_splits"));
159 }
160
161 #[test]
162 fn test_set_active_cascade_valid() {
163 let mut c = ShadowDebugConfig::default();
164 sd_set_active_cascade(&mut c, 2);
165 assert_eq!(c.active_cascade, 2);
166 }
167
168 #[test]
169 fn test_overlay_opacity_range() {
170 let c = ShadowDebugConfig::default();
171 assert!((0.0..=1.0).contains(&c.overlay_opacity));
172 }
173}