1use alloc::{vec, vec::Vec};
9use core::fmt::Debug;
10
11use crate::geom::{Tri, Vertex};
12use crate::math::{
13 mat::{Mat4x4, RealToProj, RealToReal},
14 vary::Vary,
15 vec::{vec3, ProjVec4, Vec3},
16};
17
18use clip::{view_frustum, Clip, ClipVert};
19use ctx::{Context, DepthSort, FaceCull};
20use raster::tri_fill;
21use shader::{FragmentShader, VertexShader};
22use stats::Stats;
23use target::Target;
24
25pub mod batch;
26pub mod cam;
27pub mod clip;
28pub mod ctx;
29pub mod raster;
30pub mod shader;
31pub mod stats;
32pub mod target;
33pub mod tex;
34
35#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
37pub struct Model;
38
39#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
41pub struct World;
42
43#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
45pub struct View;
46
47#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
49pub struct Ndc;
50
51#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
53pub struct Screen;
54
55pub type ModelToWorld = RealToReal<3, Model, World>;
57
58pub type WorldToView = RealToReal<3, World, View>;
60
61pub type ModelToView = RealToReal<3, Model, View>;
63
64pub type ModelToProj = RealToProj<Model>;
66
67pub type ViewToProj = RealToProj<View>;
69
70pub type NdcToScreen = RealToReal<3, Ndc, Screen>;
72
73pub trait Shader<Vtx, Var, Uni>:
75 VertexShader<Vtx, Uni, Output = Vertex<ProjVec4, Var>> + FragmentShader<Var>
76{
77}
78impl<S, Vtx, Var, Uni> Shader<Vtx, Var, Uni> for S where
79 S: VertexShader<Vtx, Uni, Output = Vertex<ProjVec4, Var>>
80 + FragmentShader<Var>
81{
82}
83
84pub fn render<Vtx: Clone, Var: Vary, Uni: Copy, Shd>(
86 tris: impl AsRef<[Tri<usize>]>,
87 verts: impl AsRef<[Vtx]>,
88 shader: &Shd,
89 uniform: Uni,
90 to_screen: Mat4x4<NdcToScreen>,
91 target: &mut impl Target,
92 ctx: &Context,
93) where
94 Shd: Shader<Vtx, Var, Uni>,
95{
96 let mut stats = Stats::start();
97
98 stats.calls = 1.0;
99 stats.prims.i += tris.as_ref().len();
100 stats.verts.i += verts.as_ref().len();
101
102 let verts: Vec<_> = verts
104 .as_ref()
105 .iter()
106 .cloned()
108 .map(|v| ClipVert::new(shader.shade_vertex(v, uniform)))
109 .collect();
110
111 let tris: Vec<_> = tris
113 .as_ref()
114 .iter()
115 .map(|Tri(vs)| Tri(vs.map(|i| verts[i].clone())))
116 .collect();
117
118 let mut clipped = vec![];
120 tris.clip(&view_frustum::PLANES, &mut clipped);
121
122 if let Some(d) = ctx.depth_sort {
123 depth_sort(&mut clipped, d);
124 }
125
126 for Tri(vs) in clipped {
127 let vs = vs.map(|v| {
129 let [x, y, _, w] = v.pos.0;
130 let pos = vec3(x, y, 1.0).z_div(w);
137 Vertex {
138 pos: to_screen.apply(&pos),
140 attrib: v.attrib.z_div(w),
142 }
143 });
144
145 match ctx.face_cull {
147 Some(FaceCull::Back) if is_backface(&vs) => continue,
148 Some(FaceCull::Front) if !is_backface(&vs) => continue,
149 _ => {}
150 }
151
152 stats.prims.o += 1;
154 stats.verts.o += 3;
155
156 tri_fill(vs, |scanline| {
158 stats.frags += target.rasterize(scanline, shader, ctx);
160 });
161 }
162 *ctx.stats.borrow_mut() += stats.finish();
163}
164
165fn depth_sort<A>(tris: &mut [Tri<ClipVert<A>>], d: DepthSort) {
166 tris.sort_unstable_by(|t, u| {
167 let z = t.0[0].pos.z() + t.0[1].pos.z() + t.0[2].pos.z();
168 let w = u.0[0].pos.z() + u.0[1].pos.z() + u.0[2].pos.z();
169 if d == DepthSort::FrontToBack {
170 z.total_cmp(&w)
171 } else {
172 w.total_cmp(&z)
173 }
174 });
175}
176
177fn is_backface<V>(vs: &[Vertex<Vec3<Screen>, V>]) -> bool {
178 let v = vs[1].pos - vs[0].pos;
179 let u = vs[2].pos - vs[0].pos;
180 v[0] * u[1] - v[1] * u[0] > 0.0
181}