Skip to main content

oxihuman_viewer/
texture_mip_debug.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan) / SPDX-License-Identifier: Apache-2.0
2#![allow(dead_code)]
3
4//! Mipmap level debug visualization (color-coded levels).
5
6/// Mip level debug configuration.
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct TextureMipDebugConfig {
10    /// Enable mip debug view.
11    pub enabled: bool,
12    /// Maximum mip levels to distinguish (up to 16).
13    pub max_levels: u32,
14    /// Colors per mip level [R, G, B, A].
15    pub level_colors: Vec<[f32; 4]>,
16    /// Overlay opacity 0..=1.
17    pub opacity: f32,
18    /// Show mip level number as text.
19    pub show_numbers: bool,
20}
21
22impl Default for TextureMipDebugConfig {
23    fn default() -> Self {
24        // Deterministic palette for mip levels
25        let colors: Vec<[f32; 4]> = vec![
26            [0.2, 0.8, 0.2, 1.0], // Level 0 - green (full res)
27            [0.8, 0.8, 0.2, 1.0], // Level 1 - yellow
28            [0.8, 0.5, 0.1, 1.0], // Level 2 - orange
29            [0.8, 0.2, 0.2, 1.0], // Level 3 - red
30            [0.6, 0.2, 0.8, 1.0], // Level 4 - purple
31            [0.2, 0.4, 0.9, 1.0], // Level 5 - blue
32            [0.2, 0.8, 0.8, 1.0], // Level 6 - cyan
33            [0.5, 0.5, 0.5, 1.0], // Level 7+ - gray
34        ];
35        Self {
36            enabled: false,
37            max_levels: 8,
38            level_colors: colors,
39            opacity: 0.7,
40            show_numbers: true,
41        }
42    }
43}
44
45/// Create default config.
46#[allow(dead_code)]
47pub fn new_texture_mip_debug_config() -> TextureMipDebugConfig {
48    TextureMipDebugConfig::default()
49}
50
51/// Get color for a mip level.
52#[allow(dead_code)]
53pub fn mip_level_color(level: u32, cfg: &TextureMipDebugConfig) -> [f32; 4] {
54    if cfg.level_colors.is_empty() {
55        return [1.0, 1.0, 1.0, 1.0];
56    }
57    let idx = (level as usize).min(cfg.level_colors.len() - 1);
58    let mut c = cfg.level_colors[idx];
59    c[3] = cfg.opacity;
60    c
61}
62
63/// Enable.
64#[allow(dead_code)]
65pub fn tmd_enable(cfg: &mut TextureMipDebugConfig) {
66    cfg.enabled = true;
67}
68
69/// Disable.
70#[allow(dead_code)]
71pub fn tmd_disable(cfg: &mut TextureMipDebugConfig) {
72    cfg.enabled = false;
73}
74
75/// Set opacity.
76#[allow(dead_code)]
77pub fn tmd_set_opacity(cfg: &mut TextureMipDebugConfig, opacity: f32) {
78    cfg.opacity = opacity.clamp(0.0, 1.0);
79}
80
81/// Compute mip level from texture size ratio.
82#[allow(dead_code)]
83pub fn compute_mip_level(base_size: u32, current_size: u32) -> u32 {
84    if current_size == 0 || base_size == 0 {
85        return 0;
86    }
87    let ratio = base_size as f32 / current_size as f32;
88    ratio.log2().max(0.0) as u32
89}
90
91/// Total color entries.
92#[allow(dead_code)]
93pub fn tmd_color_count(cfg: &TextureMipDebugConfig) -> usize {
94    cfg.level_colors.len()
95}
96
97/// Serialize to JSON.
98#[allow(dead_code)]
99pub fn texture_mip_debug_to_json(cfg: &TextureMipDebugConfig) -> String {
100    format!(
101        r#"{{"enabled":{},"max_levels":{},"opacity":{:.4},"show_numbers":{}}}"#,
102        cfg.enabled, cfg.max_levels, cfg.opacity, cfg.show_numbers
103    )
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_default() {
112        let c = TextureMipDebugConfig::default();
113        assert!(!c.enabled);
114        assert_eq!(c.level_colors.len(), 8);
115    }
116
117    #[test]
118    fn test_level_color_0() {
119        let c = TextureMipDebugConfig::default();
120        let col = mip_level_color(0, &c);
121        /* level 0 green = 0.8; only alpha is replaced with opacity */
122        assert!((col[1] - 0.8).abs() < 1e-5);
123    }
124
125    #[test]
126    fn test_level_color_clamped() {
127        let c = TextureMipDebugConfig::default();
128        let last = mip_level_color(99, &c);
129        let eight = mip_level_color(7, &c);
130        assert!((last[0] - eight[0]).abs() < 1e-5);
131    }
132
133    #[test]
134    fn test_enable_disable() {
135        let mut c = TextureMipDebugConfig::default();
136        tmd_enable(&mut c);
137        assert!(c.enabled);
138        tmd_disable(&mut c);
139        assert!(!c.enabled);
140    }
141
142    #[test]
143    fn test_set_opacity() {
144        let mut c = TextureMipDebugConfig::default();
145        tmd_set_opacity(&mut c, 0.4);
146        assert!((c.opacity - 0.4).abs() < 1e-6);
147    }
148
149    #[test]
150    fn test_compute_mip_level() {
151        assert_eq!(compute_mip_level(1024, 256), 2);
152        assert_eq!(compute_mip_level(1024, 1024), 0);
153    }
154
155    #[test]
156    fn test_compute_mip_zero_size() {
157        assert_eq!(compute_mip_level(1024, 0), 0);
158    }
159
160    #[test]
161    fn test_color_count() {
162        let c = TextureMipDebugConfig::default();
163        assert_eq!(tmd_color_count(&c), 8);
164    }
165
166    #[test]
167    fn test_opacity_applied() {
168        let c = TextureMipDebugConfig {
169            opacity: 0.5,
170            ..Default::default()
171        };
172        let col = mip_level_color(0, &c);
173        assert!((col[3] - 0.5).abs() < 1e-5);
174    }
175
176    #[test]
177    fn test_to_json() {
178        let j = texture_mip_debug_to_json(&TextureMipDebugConfig::default());
179        assert!(j.contains("max_levels"));
180        assert!(j.contains("show_numbers"));
181    }
182}