1#![allow(dead_code)]
4
5use std::f32::consts::FRAC_PI_2;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[allow(dead_code)]
12pub enum BentNormalMode {
13 WorldSpace,
14 TangentSpace,
15 AoWeighted,
16}
17
18#[derive(Debug, Clone, PartialEq)]
20#[allow(dead_code)]
21pub struct BentNormalConfig {
22 pub mode: BentNormalMode,
23 pub scale: f32,
24 pub ao_threshold: f32,
25 pub enabled: bool,
26}
27
28impl Default for BentNormalConfig {
29 fn default() -> Self {
30 Self {
31 mode: BentNormalMode::WorldSpace,
32 scale: 1.0,
33 ao_threshold: 0.5,
34 enabled: false,
35 }
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Default)]
41#[allow(dead_code)]
42pub struct BentNormalEntry {
43 pub position: [f32; 3],
44 pub bent_normal: [f32; 3],
45 pub ao: f32,
46}
47
48#[derive(Debug, Clone, Default)]
50#[allow(dead_code)]
51pub struct BentNormalDebug {
52 pub config: BentNormalConfig,
53 pub entries: Vec<BentNormalEntry>,
54}
55
56#[allow(dead_code)]
58pub fn default_bent_normal_config() -> BentNormalConfig {
59 BentNormalConfig::default()
60}
61
62#[allow(dead_code)]
64pub fn new_bent_normal_debug(cfg: BentNormalConfig) -> BentNormalDebug {
65 BentNormalDebug {
66 config: cfg,
67 entries: Vec::new(),
68 }
69}
70
71#[allow(dead_code)]
73pub fn add_bent_normal(d: &mut BentNormalDebug, pos: [f32; 3], bn: [f32; 3], ao: f32) {
74 d.entries.push(BentNormalEntry {
75 position: pos,
76 bent_normal: bn,
77 ao: ao.clamp(0.0, 1.0),
78 });
79}
80
81#[allow(dead_code)]
83pub fn clear_bent_normals(d: &mut BentNormalDebug) {
84 d.entries.clear();
85}
86
87#[allow(dead_code)]
89pub fn bent_normal_count(d: &BentNormalDebug) -> usize {
90 d.entries.len()
91}
92
93#[allow(dead_code)]
95pub fn set_bent_normal_enabled(d: &mut BentNormalDebug, enabled: bool) {
96 d.config.enabled = enabled;
97}
98
99#[allow(dead_code)]
101pub fn filtered_entries(d: &BentNormalDebug) -> Vec<&BentNormalEntry> {
102 d.entries
103 .iter()
104 .filter(|e| e.ao >= d.config.ao_threshold)
105 .collect()
106}
107
108#[allow(dead_code)]
110pub fn normalize_bn(v: [f32; 3]) -> [f32; 3] {
111 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
112 if len < 1e-9 {
113 [0.0, 0.0, 1.0]
114 } else {
115 [v[0] / len, v[1] / len, v[2] / len]
116 }
117}
118
119#[allow(dead_code)]
121pub fn bent_normal_to_color(bn: [f32; 3]) -> [f32; 3] {
122 [
123 (bn[0] * 0.5 + 0.5).clamp(0.0, 1.0),
124 (bn[1] * 0.5 + 0.5).clamp(0.0, 1.0),
125 (bn[2] * 0.5 + 0.5).clamp(0.0, 1.0),
126 ]
127}
128
129#[allow(dead_code)]
131pub fn bent_normal_angle(bn: [f32; 3], surface_normal: [f32; 3]) -> f32 {
132 let dot = bn[0] * surface_normal[0] + bn[1] * surface_normal[1] + bn[2] * surface_normal[2];
133 let angle = dot.clamp(-1.0, 1.0).acos();
134 angle / FRAC_PI_2
136}
137
138#[allow(dead_code)]
140pub fn bent_normal_debug_to_json(d: &BentNormalDebug) -> String {
141 format!(
142 r#"{{"count":{},"enabled":{}}}"#,
143 d.entries.len(),
144 d.config.enabled
145 )
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn default_config_disabled() {
154 assert!(!BentNormalConfig::default().enabled);
155 }
156
157 #[test]
158 fn add_and_count() {
159 let mut d = new_bent_normal_debug(default_bent_normal_config());
160 add_bent_normal(&mut d, [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], 0.8);
161 assert_eq!(bent_normal_count(&d), 1);
162 }
163
164 #[test]
165 fn clear_empties() {
166 let mut d = new_bent_normal_debug(default_bent_normal_config());
167 add_bent_normal(&mut d, [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], 0.5);
168 clear_bent_normals(&mut d);
169 assert_eq!(bent_normal_count(&d), 0);
170 }
171
172 #[test]
173 fn enable_sets_flag() {
174 let mut d = new_bent_normal_debug(default_bent_normal_config());
175 set_bent_normal_enabled(&mut d, true);
176 assert!(d.config.enabled);
177 }
178
179 #[test]
180 fn filter_by_ao() {
181 let mut d = new_bent_normal_debug(BentNormalConfig {
182 ao_threshold: 0.6,
183 ..Default::default()
184 });
185 add_bent_normal(&mut d, [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], 0.9);
186 add_bent_normal(&mut d, [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], 0.3);
187 assert_eq!(filtered_entries(&d).len(), 1);
188 }
189
190 #[test]
191 fn normalize_unit_vector() {
192 let n = normalize_bn([0.0, 0.0, 1.0]);
193 assert!((n[2] - 1.0).abs() < 1e-6);
194 }
195
196 #[test]
197 fn normalize_zero_returns_up() {
198 let n = normalize_bn([0.0, 0.0, 0.0]);
199 assert!((n[2] - 1.0).abs() < 1e-6);
200 }
201
202 #[test]
203 fn color_up_normal() {
204 let c = bent_normal_to_color([0.0, 1.0, 0.0]);
205 assert!((c[1] - 1.0).abs() < 1e-6);
206 }
207
208 #[test]
209 fn angle_parallel_is_zero() {
210 let angle = bent_normal_angle([0.0, 1.0, 0.0], [0.0, 1.0, 0.0]);
211 assert!(angle < 1e-5);
212 }
213
214 #[test]
215 fn json_contains_count() {
216 let d = new_bent_normal_debug(default_bent_normal_config());
217 assert!(bent_normal_debug_to_json(&d).contains("count"));
218 }
219}