use core::fmt::Debug;
use embedded_graphics_core::pixelcolor::{Rgb565, RgbColor};
#[derive(Debug, Clone, Copy)]
pub struct FogConfig {
pub color: Rgb565,
pub near: u32,
pub far: u32,
}
impl FogConfig {
pub fn new(color: Rgb565, near: f32, far: f32) -> Self {
Self {
color,
near: (near * 65536.0) as u32,
far: (far * 65536.0) as u32,
}
}
#[inline]
pub fn apply(&self, base_color: Rgb565, depth: u32) -> Rgb565 {
let fog_factor = if depth <= self.near {
0u32
} else if depth >= self.far {
65536u32 } else {
let numerator = (depth - self.near) as u64;
let denominator = (self.far - self.near) as u64;
((numerator * 65536) / denominator) as u32
};
let base_r = base_color.r() as u32;
let base_g = base_color.g() as u32;
let base_b = base_color.b() as u32;
let fog_r = self.color.r() as u32;
let fog_g = self.color.g() as u32;
let fog_b = self.color.b() as u32;
let r = ((base_r * (65536 - fog_factor) + fog_r * fog_factor) / 65536) as u8;
let g = ((base_g * (65536 - fog_factor) + fog_g * fog_factor) / 65536) as u8;
let b = ((base_b * (65536 - fog_factor) + fog_b * fog_factor) / 65536) as u8;
Rgb565::new(r, g, b)
}
}
#[derive(Debug, Clone, Copy)]
pub struct FogShader<'a, S> {
pub inner: S,
pub fog: &'a FogConfig,
}
impl<'a, S: super::FragmentShader> super::FragmentShader for FogShader<'a, S> {
type Interpolants = S::Interpolants;
#[inline(always)]
fn shade(&self, x: i32, y: i32, z: crate::ZDepth, interps: Self::Interpolants) -> Rgb565 {
let base = self.inner.shade(x, y, z, interps);
self.fog.apply(base, u32::from(z))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shader::{FlatColorShader, FragmentShader};
#[test]
fn test_fog_interpolation() {
let fog_color = Rgb565::WHITE;
let base_color = Rgb565::BLACK;
let fog = FogConfig::new(fog_color, 1.0, 10.0);
assert_eq!(fog.apply(base_color, (1.0 * 65536.0) as u32), base_color);
assert_eq!(fog.apply(base_color, 0), base_color);
assert_eq!(fog.apply(base_color, (10.0 * 65536.0) as u32), fog_color);
assert_eq!(fog.apply(base_color, (20.0 * 65536.0) as u32), fog_color);
let mid_depth = (5.5 * 65536.0) as u32;
let mid_color = fog.apply(base_color, mid_depth);
assert!(mid_color.r() > 10 && mid_color.r() < 25);
}
#[test]
fn test_fog_shader_decorator() {
let fog = FogConfig::new(Rgb565::WHITE, 1.0, 5.0);
let base_shader = FlatColorShader { color: Rgb565::RED };
let fog_shader = FogShader {
inner: base_shader,
fog: &fog,
};
let near_depth = crate::to_zdepth(65536);
assert_eq!(fog_shader.shade(0, 0, near_depth, ()), Rgb565::RED);
}
}