use std::convert::TryFrom as _;
use cgmath::{Vector3, Zero as _};
use crate::camera::GraphicsOptions;
use crate::math::Rgba;
use crate::space::SpaceBlockData;
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct RtOptionsRef<'a, C> {
pub graphics_options: &'a GraphicsOptions,
pub custom_options: &'a C,
}
impl<'a, C> Copy for RtOptionsRef<'a, C> {}
impl<'a, C> Clone for RtOptionsRef<'a, C> {
fn clone(&self) -> Self {
*self
}
}
pub trait PixelBuf: Default {
type BlockData: RtBlockData;
fn opaque(&self) -> bool;
fn add(&mut self, surface_color: Rgba, block_data: &Self::BlockData);
fn hit_nothing(&mut self) {}
fn paint(
color: Rgba,
options: RtOptionsRef<'_, <Self::BlockData as RtBlockData>::Options>,
) -> Self
where
Self: Sized,
{
let mut result = Self::default();
result.add(color, &Self::BlockData::sky(options));
result
}
fn mean<const N: usize>(items: [Self; N]) -> Self;
}
pub trait RtBlockData: Send + Sync {
type Options: Send + Sync;
fn from_block(options: RtOptionsRef<'_, Self::Options>, block: &SpaceBlockData) -> Self;
fn error(options: RtOptionsRef<'_, Self::Options>) -> Self;
fn sky(options: RtOptionsRef<'_, Self::Options>) -> Self;
}
impl RtBlockData for () {
type Options = ();
fn from_block(_: RtOptionsRef<'_, Self::Options>, _: &SpaceBlockData) -> Self {}
fn error(_: RtOptionsRef<'_, Self::Options>) -> Self {}
fn sky(_: RtOptionsRef<'_, Self::Options>) -> Self {}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ColorBuf {
color_accumulator: Vector3<f32>,
ray_alpha: f32,
}
impl PixelBuf for ColorBuf {
type BlockData = ();
#[inline]
fn opaque(&self) -> bool {
self.ray_alpha < 1.0 / 256.0
}
#[inline]
fn add(&mut self, surface_color: Rgba, _block_data: &Self::BlockData) {
let color_vector: Vector3<f32> = surface_color.to_rgb().into();
let surface_alpha = surface_color.alpha().into_inner();
let alpha_for_add = surface_alpha * self.ray_alpha;
self.ray_alpha *= 1.0 - surface_alpha;
self.color_accumulator += color_vector * alpha_for_add;
}
#[inline]
fn mean<const N: usize>(items: [Self; N]) -> Self {
Self {
color_accumulator: items
.iter()
.map(|cb| cb.color_accumulator)
.sum::<Vector3<f32>>()
/ (N as f32),
ray_alpha: items.iter().map(|cb| cb.ray_alpha).sum::<f32>() / (N as f32),
}
}
}
impl Default for ColorBuf {
#[inline]
fn default() -> Self {
Self {
color_accumulator: Vector3::zero(),
ray_alpha: 1.0,
}
}
}
impl From<ColorBuf> for Rgba {
fn from(buf: ColorBuf) -> Rgba {
if buf.ray_alpha >= 1.0 {
Rgba::TRANSPARENT
} else {
let color_alpha = 1.0 - buf.ray_alpha;
let non_premultiplied_color = buf.color_accumulator / color_alpha;
Rgba::try_from(non_premultiplied_color.extend(color_alpha))
.unwrap_or_else(|_| Rgba::new(1.0, 0.0, 0.0, 1.0))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn color_buf() {
let color_1 = Rgba::new(1.0, 0.0, 0.0, 0.75);
let color_2 = Rgba::new(0.0, 1.0, 0.0, 0.5);
let color_3 = Rgba::new(0.0, 0.0, 1.0, 1.0);
let mut buf = ColorBuf::default();
assert_eq!(Rgba::from(buf), Rgba::TRANSPARENT);
assert!(!buf.opaque());
buf.add(color_1, &());
assert_eq!(Rgba::from(buf), color_1);
assert!(!buf.opaque());
buf.add(color_2, &());
assert!(!buf.opaque());
buf.add(color_3, &());
assert!(Rgba::from(buf).fully_opaque());
assert!(buf.opaque());
}
}