use alloc::vec::Vec;
use core::fmt::Debug;
use crate::geom::Vertex;
use crate::math::{
Mat4x4, Vary,
mat::{RealToProj, RealToReal},
vec::ProjVec3,
};
use self::{
clip::{ClipVert, view_frustum},
ctx::DepthSort,
raster::Scanline,
};
pub use self::{
batch::Batch,
cam::Camera,
clip::Clip,
ctx::Context,
shader::{FragmentShader, VertexShader},
stats::Stats,
target::{Colorbuf, Framebuf, Target},
tex::{TexCoord, Texture, uv},
text::Text,
};
pub mod batch;
pub mod cam;
pub mod clip;
pub mod ctx;
pub mod prim;
pub mod raster;
pub mod scene;
pub mod shader;
pub mod stats;
pub mod target;
pub mod tex;
pub mod text;
pub trait Render<V: Vary> {
type Clip;
type Clips: Clip<Item = Self::Clip> + ?Sized;
type Screen;
fn inline(ixd: Self, vs: &[ClipVert<V>]) -> Self::Clip;
fn depth(_clip: &Self::Clip) -> f32 {
f32::INFINITY
}
fn is_backface(_: &Self::Screen) -> bool {
false
}
fn to_screen(clip: Self::Clip, tf: &Mat4x4<NdcToScreen>) -> Self::Screen;
fn rasterize<F: FnMut(Scanline<V>)>(scr: Self::Screen, scanline_fn: F);
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Model;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct World;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct View;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Ndc;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Screen;
pub type ModelToWorld = RealToReal<3, Model, World>;
pub type WorldToView = RealToReal<3, World, View>;
pub type ModelToView = RealToReal<3, Model, View>;
pub type ModelToProj = RealToProj<Model>;
pub type ViewToProj = RealToProj<View>;
pub type NdcToScreen = RealToReal<3, Ndc, Screen>;
pub trait Shader<Vtx, Var, Uni>:
VertexShader<Vtx, Uni, Output = Vertex<ProjVec3, Var>> + FragmentShader<Var>
{
}
impl<S, Vtx, Var, Uni> Shader<Vtx, Var, Uni> for S where
S: VertexShader<Vtx, Uni, Output = Vertex<ProjVec3, Var>>
+ FragmentShader<Var>
{
}
pub fn render<Prim, Vtx: Clone, Var, Uni: Copy, Shd>(
prims: impl AsRef<[Prim]>,
verts: impl AsRef<[Vtx]>,
shader: &Shd,
uniform: Uni,
to_screen: Mat4x4<NdcToScreen>,
target: &mut impl Target,
ctx: &Context,
) where
Prim: Render<Var> + Clone,
[<Prim>::Clip]: Clip<Item = Prim::Clip>,
Var: Vary,
Shd: Shader<Vtx, Var, Uni>,
{
let verts = verts.as_ref();
let prims = prims.as_ref();
let mut stats = Stats::start();
stats.calls = 1.0;
stats.prims.i = prims.len();
stats.verts.i = verts.len();
let verts: Vec<_> = verts
.iter()
.cloned()
.map(|v| shader.shade_vertex(v, uniform))
.map(ClipVert::new)
.collect();
let prims: Vec<_> = prims
.iter()
.map(|tri| Prim::inline(tri.clone(), &verts))
.collect();
let mut clipped = Vec::with_capacity(prims.len() / 2);
view_frustum::clip(&prims[..], &mut clipped);
if let Some(d) = ctx.depth_sort {
depth_sort::<Prim, _>(&mut clipped, d);
}
for prim in clipped {
let prim = Prim::to_screen(prim, &to_screen);
if ctx.face_cull(Prim::is_backface(&prim)) {
continue;
}
stats.prims.o += 1;
stats.verts.o += 3;
Prim::rasterize(prim, |scanline| {
stats.frags += target.rasterize(scanline, shader, ctx);
});
}
*ctx.stats.borrow_mut() += stats.finish();
}
fn depth_sort<P: Render<V>, V: Vary>(prims: &mut [P::Clip], d: DepthSort) {
prims.sort_unstable_by(|t, u| {
let z = P::depth(t);
let w = P::depth(u);
if d == DepthSort::FrontToBack {
z.total_cmp(&w)
} else {
w.total_cmp(&z)
}
});
}