euc/rasterizer/
lines.rs

1use super::*;
2use crate::{Interpolate, Pipeline, Target};
3use core::marker::PhantomData;
4use vek::*;
5
6/// A rasterizer that produces straight lines from groups of 2 consecutive vertices.
7pub struct Lines<'a, D> {
8    phantom: PhantomData<&'a D>,
9}
10
11impl<'a, D: Target<Item = f32>> Rasterizer for Lines<'a, D> {
12    type Input = [f32; 3]; // Vertex coordinates
13    type Supplement = Option<&'a mut D>; // Depth buffer
14
15    fn draw<P: Pipeline, T: Target<Item = P::Pixel>>(
16        pipeline: &P,
17        vertices: &[P::Vertex],
18        target: &mut T,
19        mut depth: Self::Supplement,
20    ) {
21        if let Some(depth) = depth.as_ref() {
22            assert_eq!(
23                target.size(),
24                depth.size(),
25                "Target and depth buffers are not similarly sized!"
26            );
27        }
28
29        let size = Vec2::from(target.size());
30        let half_scr = size.map(|e: usize| e as f32 * 0.5);
31        const MIRROR: Vec2<f32> = Vec2 { x: 1.0, y: -1.0 };
32
33        vertices.chunks_exact(2).for_each(|verts| {
34            // Compute vertex shader outputs
35            let (a_hom, a_vs_out) = pipeline.vert(&verts[0]);
36            let (b_hom, b_vs_out) = pipeline.vert(&verts[1]);
37
38            // Convert homogenous to euclidean coordinates
39            let a = Vec3::new(a_hom[0], a_hom[1], a_hom[2]) / a_hom[3];
40            let b = Vec3::new(b_hom[0], b_hom[1], b_hom[2]) / b_hom[3];
41
42            // Convert to framebuffer coordinates
43            let a_scr = half_scr * (Vec2::from(a) * MIRROR + 1.0);
44            let b_scr = half_scr * (Vec2::from(b) * MIRROR + 1.0);
45
46            let pa: Vec4<f32> = Vec4::from(a_hom);
47            let pb: Vec4<f32> = Vec4::from(b_hom);
48            let ab: Vec4<f32> = pb - pa;
49
50            let a_px = a_scr.map(|e| e as i32);
51            let b_px = b_scr.map(|e| e as i32);
52
53            let min = Vec2::<i32>::min(a_px, b_px);
54            let max = Vec2::<i32>::max(a_px, b_px);
55
56            if (max.x - min.x) > (max.y - min.y) {
57                let (l_scr, r_scr) = if a_scr.x < b_scr.x {
58                    (a_scr, b_scr)
59                } else {
60                    (b_scr, a_scr)
61                };
62                let factor = 1.0 / (r_scr.x - l_scr.x);
63                let m = Mat2::new(pa.w, -pa.x, -ab.w, ab.x) / (ab.x * pa.w - pa.x * ab.w);
64
65                for x in l_scr.x as i32..r_scr.x as i32 {
66                    let y = l_scr.y + (x as f32 - l_scr.x) * factor * (r_scr.y - l_scr.y);
67
68                    // TODO: This is really bad bounds test code, fix this
69                    if x < 0 || (x as usize) >= size.x || y < 0.0 || (y as usize) >= size.y {
70                        continue;
71                    }
72
73                    // Calculate the interpolated depth of this fragment
74                    let s_hom = m * Vec2::new(2.0 * x as f32 / size.x as f32 - 1.0, 1.0);
75                    let s = s_hom.x / s_hom.y;
76                    let z_lerped = (pa.z + s * ab.z) * s_hom.y;
77
78                    let (x, y) = (x as usize, y as usize);
79
80                    // Depth test
81                    if depth.as_ref().map(|depth| z_lerped <= unsafe { depth.get([x, y]) }).unwrap_or(true) {
82                        // Calculate the interpolated vertex attributes of this fragment
83                        let vs_out_lerped =
84                            P::VsOut::lerp2(a_vs_out.clone(), b_vs_out.clone(), 1.0 - s, s);
85
86                        unsafe {
87                            depth.as_mut().map(|depth| depth.set([x, y], z_lerped));
88                            target.set([x, y], pipeline.frag(&vs_out_lerped));
89                        }
90                    }
91                }
92            } else {
93                let (l_scr, r_scr) = if a_scr.y < b_scr.y {
94                    (a_scr, b_scr)
95                } else {
96                    (b_scr, a_scr)
97                };
98
99                let factor = 1.0 / (r_scr.y - l_scr.y);
100                let m = Mat2::new(pa.w, -pa.y, -ab.w, ab.y) / (ab.y * pa.w - pa.y * ab.w);
101
102                for y in l_scr.y as i32..r_scr.y as i32 {
103                    let x = l_scr.x + (y as f32 - l_scr.y) * factor * (r_scr.x - l_scr.x);
104
105                    // TODO: This is really bad bounds test code, fix this
106                    if x < 0.0 || (x as usize) >= size.x || y < 0 || (y as usize) >= size.y {
107                        continue;
108                    }
109
110                    // Calculate the interpolated depth of this fragment
111                    let s_hom = m * Vec2::new(-2.0 * y as f32 / size.y as f32 + 1.0, 1.0);
112                    let s = s_hom.x / s_hom.y;
113                    let z_lerped = (pa.z + s * ab.z) * s_hom.y;
114
115                    let (x, y) = (x as usize, y as usize);
116
117                    // Depth test
118                    if depth.as_ref().map(|depth| z_lerped < unsafe { depth.get([x, y]) }).unwrap_or(true) {
119                        // Calculate the interpolated vertex attributes of this fragment
120                        let vs_out_lerped =
121                            P::VsOut::lerp2(a_vs_out.clone(), b_vs_out.clone(), 1.0 - s, s);
122
123                        unsafe {
124                            depth.as_mut().map(|depth| depth.set([x, y], z_lerped));
125                            target.set([x, y], P::frag(pipeline, &vs_out_lerped));
126                        }
127                    }
128                }
129            }
130        });
131    }
132}