pub fn height_to_normal(heights: &[f64], width: u32, height: u32, strength: f32) -> Vec<u8> {
let w = width as usize;
let h = height as usize;
let s = strength as f64;
let mut out = vec![0u8; w * h * 4];
for y in 0..h {
for x in 0..w {
let xm = (x + w - 1) % w;
let xp = (x + 1) % w;
let ym = (y + h - 1) % h;
let yp = (y + 1) % h;
let left = heights[y * w + xm]; let right = heights[y * w + xp]; let above = heights[ym * w + x]; let below = heights[yp * w + x];
let dx = (right - left) * s;
let dy = (below - above) * s;
let len = (dx * dx + dy * dy + 1.0).sqrt();
let nx = -dx / len;
let ny = -dy / len;
let nz = 1.0 / len;
let idx = (y * w + x) * 4;
out[idx] = encode_normal(nx);
out[idx + 1] = encode_normal(ny);
out[idx + 2] = encode_normal(nz);
out[idx + 3] = 255;
}
}
out
}
#[inline]
fn encode_normal(n: f64) -> u8 {
((n * 0.5 + 0.5).clamp(0.0, 1.0) * 255.0).round() as u8
}