retrofire_core/render/
prim.rs

1//! Render impls for primitives and related items.
2
3use crate::geom::{Edge, Tri, Vertex, Winding};
4use crate::math::{Apply, Mat4x4, Vary, pt3, vary::ZDiv};
5
6use super::{
7    NdcToScreen, Render,
8    clip::ClipVert,
9    raster::{Scanline, ScreenPt, line, tri_fill},
10};
11
12impl<V: Vary> Render<V> for Tri<usize> {
13    type Clip = Tri<ClipVert<V>>;
14    type Clips = [Tri<ClipVert<V>>];
15    type Screen = Tri<Vertex<ScreenPt, V>>;
16
17    fn inline(ixd: Tri<usize>, vs: &[ClipVert<V>]) -> Tri<ClipVert<V>> {
18        Tri(ixd.0.map(|i| vs[i].clone()))
19    }
20
21    fn depth(Tri([a, b, c]): &Self::Clip) -> f32 {
22        (a.pos.z() + b.pos.z() + c.pos.z()) / 3.0
23    }
24
25    fn is_backface(tri: &Self::Screen) -> bool {
26        tri.winding() == Winding::Cw
27    }
28
29    fn to_screen(
30        clip: Tri<ClipVert<V>>,
31        tf: &Mat4x4<NdcToScreen>,
32    ) -> Self::Screen {
33        Tri(to_screen(clip.0, tf))
34    }
35
36    fn rasterize<F: FnMut(Scanline<V>)>(scr: Self::Screen, scanline_fn: F) {
37        tri_fill(scr.0, scanline_fn);
38    }
39}
40
41impl<V: Vary> Render<V> for Edge<usize> {
42    type Clip = Edge<ClipVert<V>>;
43
44    type Clips = [Self::Clip];
45
46    type Screen = Edge<Vertex<ScreenPt, V>>;
47
48    fn inline(Edge(i, j): Edge<usize>, vs: &[ClipVert<V>]) -> Self::Clip {
49        Edge(vs[i].clone(), vs[j].clone())
50    }
51
52    fn to_screen(e: Self::Clip, tf: &Mat4x4<NdcToScreen>) -> Self::Screen {
53        let [a, b] = to_screen([e.0, e.1], tf);
54        Edge(a, b)
55    }
56
57    fn rasterize<F: FnMut(Scanline<V>)>(e: Self::Screen, scanline_fn: F) {
58        line([e.0, e.1], scanline_fn);
59    }
60}
61
62pub fn to_screen<V: ZDiv, const N: usize>(
63    vs: [ClipVert<V>; N],
64    tf: &Mat4x4<NdcToScreen>,
65) -> [Vertex<ScreenPt, V>; N] {
66    vs.map(|v| {
67        let [x, y, _, w] = v.pos.0;
68        // Perspective division (projection to the real plane)
69        //
70        // We use the screen-space z coordinate to store the reciprocal
71        // of the original view-space depth. The interpolated reciprocal
72        // is used in fragment processing for depth testing (larger values
73        // are closer) and for perspective correction of the varyings.
74        //
75        // TODO Ad-hoc conversion from clip space vector to screen space point.
76        //      This should be a typed conversion from projective to real space.
77        //      Vec3 was used here, which only worked because apply used to use
78        //      w=1 for vectors. Fixing it made viewport transform incorrect.
79        //      The z-div concept and trait likely need clarification.
80        let pos = pt3(x, y, 1.0).z_div(w);
81        Vertex {
82            // Viewport transform
83            pos: tf.apply(&pos),
84            // Perspective correction
85            attrib: v.attrib.z_div(w),
86        }
87    })
88}