use crate::ZDepth;
use embedded_graphics_core::pixelcolor::Rgb565;
use super::FragmentShader;
use super::blend::fast_blend_rgb565;
#[derive(Debug, Clone, Copy)]
pub struct WaterReflectConfig<'a> {
pub reference_buffer: &'a [Rgb565],
pub width: usize,
pub height: usize,
pub waterline_y: i32,
pub surface_color: Rgb565,
pub alpha: u8,
pub ripple_amplitude: i32,
pub ripple_phase: i32,
}
impl<'a> WaterReflectConfig<'a> {
pub fn new(
reference_buffer: &'a [Rgb565],
width: usize,
height: usize,
waterline_y: i32,
surface_color: Rgb565,
alpha: u8,
) -> Self {
Self {
reference_buffer,
width,
height,
waterline_y,
surface_color,
alpha,
ripple_amplitude: 0,
ripple_phase: 0,
}
}
pub fn with_ripples(mut self, amplitude: i32, phase: i32) -> Self {
self.ripple_amplitude = amplitude;
self.ripple_phase = phase;
self
}
#[inline]
pub fn sample_reflection(&self, x: i32, y: i32) -> Rgb565 {
if self.reference_buffer.is_empty() || self.width == 0 || self.height == 0 {
return self.surface_color;
}
let raw_mirror_y = 2 * self.waterline_y - y;
let (ripple_x, ripple_y) = if self.ripple_amplitude > 0 {
let offset_x = ((x + self.ripple_phase) & 7) - 3;
let offset_y = ((y + self.ripple_phase) & 7) - 3;
(
(offset_x * self.ripple_amplitude) / 4,
(offset_y * self.ripple_amplitude) / 4,
)
} else {
(0, 0)
};
let mirror_x = (x + ripple_x).clamp(0, self.width as i32 - 1) as usize;
let mirror_y = (raw_mirror_y + ripple_y).clamp(0, self.height as i32 - 1) as usize;
let idx = mirror_y * self.width + mirror_x;
if idx < self.reference_buffer.len() {
self.reference_buffer[idx]
} else {
self.surface_color
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct WaterReflectShader<'a, S> {
pub inner: S,
pub config: &'a WaterReflectConfig<'a>,
}
impl<'a, S: FragmentShader> FragmentShader for WaterReflectShader<'a, S> {
type Interpolants = S::Interpolants;
#[inline(always)]
fn shade(&self, x: i32, y: i32, z: ZDepth, interps: Self::Interpolants) -> Rgb565 {
let base_color = self.inner.shade(x, y, z, interps);
if y < self.config.waterline_y {
return base_color;
}
let reflected_color = self.config.sample_reflection(x, y);
if self.config.alpha == 0 {
reflected_color
} else if self.config.alpha == 255 {
base_color
} else {
fast_blend_rgb565(reflected_color, base_color, self.config.alpha)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shader::FlatColorShader;
use embedded_graphics_core::pixelcolor::RgbColor;
#[test]
fn test_water_reflect_mirror_sampling() {
let mut buffer = [Rgb565::BLACK; 16];
for x in 0..4 {
buffer[0 * 4 + x] = Rgb565::RED;
buffer[1 * 4 + x] = Rgb565::GREEN;
buffer[2 * 4 + x] = Rgb565::BLUE;
buffer[3 * 4 + x] = Rgb565::WHITE;
}
let config = WaterReflectConfig::new(&buffer, 4, 4, 2, Rgb565::CYAN, 0);
let shader = WaterReflectShader {
inner: FlatColorShader {
color: Rgb565::CYAN,
},
config: &config,
};
let color_above = shader.shade(0, 1, 0, ());
assert_eq!(color_above, Rgb565::CYAN);
let color_at_waterline = shader.shade(0, 2, 0, ());
assert_eq!(color_at_waterline, Rgb565::BLUE);
let color_below_waterline = shader.shade(0, 3, 0, ());
assert_eq!(color_below_waterline, Rgb565::GREEN);
}
}