1#[allow(dead_code)]
8#[derive(Clone, PartialEq, Debug)]
9pub enum BumpMapMode {
10 HeightMap,
11 DisplacementMap,
12}
13
14#[allow(dead_code)]
16pub struct BumpMapConfig {
17 pub width: u32,
18 pub height: u32,
19 pub mode: BumpMapMode,
20 pub scale: f32,
21}
22
23#[allow(dead_code)]
25pub struct BumpMapBuffer {
26 pub pixels: Vec<f32>,
27 pub width: u32,
28 pub height: u32,
29}
30
31#[allow(dead_code)]
33pub struct BumpMapRange {
34 pub min: f32,
35 pub max: f32,
36}
37
38#[allow(dead_code)]
40pub type NormalVector = [f32; 3];
41
42#[allow(dead_code)]
43pub fn default_bump_map_config(width: u32, height: u32) -> BumpMapConfig {
44 BumpMapConfig {
45 width,
46 height,
47 mode: BumpMapMode::HeightMap,
48 scale: 1.0,
49 }
50}
51
52#[allow(dead_code)]
53pub fn new_bump_map_buffer(width: u32, height: u32) -> BumpMapBuffer {
54 let count = (width * height) as usize;
55 BumpMapBuffer {
56 pixels: vec![0.0; count],
57 width,
58 height,
59 }
60}
61
62#[allow(dead_code)]
63pub fn set_bump_value(buffer: &mut BumpMapBuffer, x: u32, y: u32, value: f32) {
64 if x < buffer.width && y < buffer.height {
65 let idx = (y * buffer.width + x) as usize;
66 buffer.pixels[idx] = value;
67 }
68}
69
70#[allow(dead_code)]
71pub fn get_bump_value(buffer: &BumpMapBuffer, x: u32, y: u32) -> f32 {
72 if x < buffer.width && y < buffer.height {
73 let idx = (y * buffer.width + x) as usize;
74 buffer.pixels[idx]
75 } else {
76 0.0
77 }
78}
79
80#[allow(dead_code)]
83pub fn bump_from_positions(
84 positions: &[[f32; 3]],
85 uvs: &[[f32; 2]],
86 plane_point: [f32; 3],
87 plane_normal: [f32; 3],
88 width: u32,
89 height: u32,
90) -> BumpMapBuffer {
91 let mut buffer = new_bump_map_buffer(width, height);
92 let count = positions.len().min(uvs.len());
93 let nlen = (plane_normal[0] * plane_normal[0]
94 + plane_normal[1] * plane_normal[1]
95 + plane_normal[2] * plane_normal[2])
96 .sqrt()
97 .max(1e-8);
98 let nn = [
99 plane_normal[0] / nlen,
100 plane_normal[1] / nlen,
101 plane_normal[2] / nlen,
102 ];
103 for i in 0..count {
104 let dx = positions[i][0] - plane_point[0];
105 let dy = positions[i][1] - plane_point[1];
106 let dz = positions[i][2] - plane_point[2];
107 let h = dx * nn[0] + dy * nn[1] + dz * nn[2];
108 let u = uvs[i][0].clamp(0.0, 1.0);
109 let v = uvs[i][1].clamp(0.0, 1.0);
110 let px = (u * (width as f32 - 1.0)).round() as u32;
111 let py = (v * (height as f32 - 1.0)).round() as u32;
112 set_bump_value(&mut buffer, px, py, h);
113 }
114 buffer
115}
116
117#[allow(dead_code)]
119pub fn encode_bump_map_ppm(buffer: &BumpMapBuffer) -> Vec<u8> {
120 let header = format!("P5\n{} {}\n255\n", buffer.width, buffer.height);
121 let mut out = header.into_bytes();
122 for &val in &buffer.pixels {
123 let byte = (val.clamp(0.0, 1.0) * 255.0).round() as u8;
124 out.push(byte);
125 }
126 out
127}
128
129#[allow(dead_code)]
132pub fn bump_to_normal_map(buffer: &BumpMapBuffer, strength: f32) -> Vec<NormalVector> {
133 let w = buffer.width as i32;
134 let h = buffer.height as i32;
135 let count = (buffer.width * buffer.height) as usize;
136 let mut normals = Vec::with_capacity(count);
137
138 let sample = |x: i32, y: i32| -> f32 {
139 let cx = x.clamp(0, w - 1) as u32;
140 let cy = y.clamp(0, h - 1) as u32;
141 get_bump_value(buffer, cx, cy)
142 };
143
144 for y in 0..h {
145 for x in 0..w {
146 let left = sample(x - 1, y);
147 let right = sample(x + 1, y);
148 let up = sample(x, y - 1);
149 let down = sample(x, y + 1);
150 let dx = (right - left) * strength;
151 let dy = (down - up) * strength;
152 let nz = 1.0f32;
153 let len = (dx * dx + dy * dy + nz * nz).sqrt().max(1e-8);
154 normals.push([-dx / len, -dy / len, nz / len]);
155 }
156 }
157 normals
158}
159
160#[allow(dead_code)]
162pub fn scale_bump_values(buffer: &mut BumpMapBuffer, factor: f32) {
163 for px in buffer.pixels.iter_mut() {
164 *px *= factor;
165 }
166}
167
168#[allow(dead_code)]
170pub fn invert_bump_map(buffer: &mut BumpMapBuffer) {
171 for px in buffer.pixels.iter_mut() {
172 *px = 1.0 - *px;
173 }
174}
175
176#[allow(dead_code)]
178pub fn blur_bump_map(buffer: &mut BumpMapBuffer, radius: u32) {
179 if radius == 0 {
180 return;
181 }
182 let w = buffer.width;
183 let h = buffer.height;
184 let r = radius as i32;
185 let src = buffer.pixels.clone();
186
187 for y in 0..h {
188 for x in 0..w {
189 let mut sum = 0.0f64;
190 let mut count = 0u32;
191 for dy in -r..=r {
192 for dx in -r..=r {
193 let sx = (x as i32 + dx).clamp(0, w as i32 - 1) as u32;
194 let sy = (y as i32 + dy).clamp(0, h as i32 - 1) as u32;
195 sum += src[(sy * w + sx) as usize] as f64;
196 count += 1;
197 }
198 }
199 buffer.pixels[(y * w + x) as usize] = (sum / count as f64) as f32;
200 }
201 }
202}
203
204#[allow(dead_code)]
206pub fn bump_map_range(buffer: &BumpMapBuffer) -> BumpMapRange {
207 if buffer.pixels.is_empty() {
208 return BumpMapRange { min: 0.0, max: 0.0 };
209 }
210 let mut min = f32::MAX;
211 let mut max = f32::MIN;
212 for &v in &buffer.pixels {
213 if v < min {
214 min = v;
215 }
216 if v > max {
217 max = v;
218 }
219 }
220 BumpMapRange { min, max }
221}
222
223#[allow(dead_code)]
225pub fn bump_map_pixel_count(buffer: &BumpMapBuffer) -> usize {
226 buffer.pixels.len()
227}
228
229#[allow(dead_code)]
231pub fn clamp_bump_values(buffer: &mut BumpMapBuffer, lo: f32, hi: f32) {
232 for px in buffer.pixels.iter_mut() {
233 *px = px.clamp(lo, hi);
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_default_config() {
243 let cfg = default_bump_map_config(512, 256);
244 assert_eq!(cfg.width, 512);
245 assert_eq!(cfg.height, 256);
246 assert_eq!(cfg.mode, BumpMapMode::HeightMap);
247 assert!((cfg.scale - 1.0).abs() < f32::EPSILON);
248 }
249
250 #[test]
251 fn test_new_buffer() {
252 let buf = new_bump_map_buffer(8, 8);
253 assert_eq!(buf.width, 8);
254 assert_eq!(buf.height, 8);
255 assert_eq!(buf.pixels.len(), 64);
256 }
257
258 #[test]
259 fn test_set_get_value() {
260 let mut buf = new_bump_map_buffer(4, 4);
261 set_bump_value(&mut buf, 2, 1, 0.5);
262 assert!((get_bump_value(&buf, 2, 1) - 0.5).abs() < f32::EPSILON);
263 }
264
265 #[test]
266 fn test_get_out_of_bounds() {
267 let buf = new_bump_map_buffer(4, 4);
268 assert!((get_bump_value(&buf, 10, 10) - 0.0).abs() < f32::EPSILON);
269 }
270
271 #[test]
272 fn test_set_out_of_bounds() {
273 let mut buf = new_bump_map_buffer(4, 4);
274 set_bump_value(&mut buf, 10, 10, 1.0);
275 for &px in &buf.pixels {
276 assert!((px - 0.0).abs() < f32::EPSILON);
277 }
278 }
279
280 #[test]
281 fn test_bump_from_positions() {
282 let positions = [[0.0f32, 0.0, 1.0], [1.0, 0.0, 2.0]];
283 let uvs = [[0.0f32, 0.0], [1.0, 1.0]];
284 let buf = bump_from_positions(&positions, &uvs, [0.0, 0.0, 0.0], [0.0, 0.0, 1.0], 8, 8);
285 assert_eq!(buf.width, 8);
286 assert!((get_bump_value(&buf, 0, 0) - 1.0).abs() < f32::EPSILON);
287 assert!((get_bump_value(&buf, 7, 7) - 2.0).abs() < f32::EPSILON);
288 }
289
290 #[test]
291 fn test_encode_ppm_starts_with_p5() {
292 let buf = new_bump_map_buffer(2, 2);
293 let ppm = encode_bump_map_ppm(&buf);
294 assert!(ppm.starts_with(b"P5"));
295 }
296
297 #[test]
298 fn test_encode_ppm_size() {
299 let buf = new_bump_map_buffer(4, 4);
300 let ppm = encode_bump_map_ppm(&buf);
301 let header = "P5\n4 4\n255\n".to_string();
302 assert_eq!(ppm.len(), header.len() + 16);
303 }
304
305 #[test]
306 fn test_bump_to_normal_map() {
307 let mut buf = new_bump_map_buffer(4, 4);
308 for px in buf.pixels.iter_mut() {
310 *px = 0.5;
311 }
312 let normals = bump_to_normal_map(&buf, 1.0);
313 assert_eq!(normals.len(), 16);
314 for n in &normals {
315 assert!((n[2] - 1.0).abs() < 0.01);
316 }
317 }
318
319 #[test]
320 fn test_scale_bump_values() {
321 let mut buf = new_bump_map_buffer(2, 2);
322 for px in buf.pixels.iter_mut() {
323 *px = 0.5;
324 }
325 scale_bump_values(&mut buf, 2.0);
326 for &px in &buf.pixels {
327 assert!((px - 1.0).abs() < f32::EPSILON);
328 }
329 }
330
331 #[test]
332 fn test_invert_bump_map() {
333 let mut buf = new_bump_map_buffer(2, 2);
334 set_bump_value(&mut buf, 0, 0, 0.3);
335 invert_bump_map(&mut buf);
336 assert!((get_bump_value(&buf, 0, 0) - 0.7).abs() < 1e-6);
337 assert!((get_bump_value(&buf, 1, 0) - 1.0).abs() < f32::EPSILON);
339 }
340
341 #[test]
342 fn test_blur_bump_map() {
343 let mut buf = new_bump_map_buffer(4, 4);
344 set_bump_value(&mut buf, 2, 2, 1.0);
345 blur_bump_map(&mut buf, 1);
346 let center = get_bump_value(&buf, 2, 2);
348 assert!(center < 1.0);
349 assert!(center > 0.0);
350 }
351
352 #[test]
353 fn test_blur_radius_zero() {
354 let mut buf = new_bump_map_buffer(4, 4);
355 set_bump_value(&mut buf, 1, 1, 0.5);
356 blur_bump_map(&mut buf, 0);
357 assert!((get_bump_value(&buf, 1, 1) - 0.5).abs() < f32::EPSILON);
358 }
359
360 #[test]
361 fn test_bump_map_range() {
362 let mut buf = new_bump_map_buffer(4, 4);
363 set_bump_value(&mut buf, 0, 0, 0.1);
364 set_bump_value(&mut buf, 1, 0, 0.9);
365 let range = bump_map_range(&buf);
366 assert!((range.min - 0.0).abs() < f32::EPSILON);
367 assert!((range.max - 0.9).abs() < f32::EPSILON);
368 }
369
370 #[test]
371 fn test_bump_map_range_empty() {
372 let buf = BumpMapBuffer {
373 pixels: vec![],
374 width: 0,
375 height: 0,
376 };
377 let range = bump_map_range(&buf);
378 assert!((range.min - 0.0).abs() < f32::EPSILON);
379 assert!((range.max - 0.0).abs() < f32::EPSILON);
380 }
381
382 #[test]
383 fn test_bump_map_pixel_count() {
384 let buf = new_bump_map_buffer(8, 4);
385 assert_eq!(bump_map_pixel_count(&buf), 32);
386 }
387
388 #[test]
389 fn test_clamp_bump_values() {
390 let mut buf = new_bump_map_buffer(2, 2);
391 set_bump_value(&mut buf, 0, 0, -0.5);
392 set_bump_value(&mut buf, 1, 0, 1.5);
393 clamp_bump_values(&mut buf, 0.0, 1.0);
394 assert!((get_bump_value(&buf, 0, 0) - 0.0).abs() < f32::EPSILON);
395 assert!((get_bump_value(&buf, 1, 0) - 1.0).abs() < f32::EPSILON);
396 }
397}