Skip to main content

ling/gfx/
depth.rs

1// src/gfx/depth.rs — deferred depth-sorted draw queue (painter's algorithm).
2//
3// All 3-D draw calls (`วาดสามเหลี่ยม3มิติ`, `วาดเส้น3มิติ`) push a `DrawCall`
4// into this queue instead of rasterising immediately.  When `แสดงผล` / `present`
5// is called, the queue is sorted back-to-front by the depth tag and then
6// flushed into the pixel buffer.
7//
8// Painter's algorithm is exact for convex non-intersecting geometry and
9// produces plausible results for the Sierpiński fractal + tesseract wireframe.
10
11#[cfg(not(target_arch = "wasm32"))]
12use crate::gfx::raster;
13
14/// Tagged draw call stored in the queue.
15#[derive(Debug, Clone)]
16pub struct DrawCall {
17    /// Camera-space z of the face/edge centroid — larger = further away.
18    pub depth: f32,
19    /// Pre-lit 0x00RRGGBB colour.
20    pub color: u32,
21    pub kind:  DrawKind,
22}
23
24#[derive(Debug, Clone)]
25pub enum DrawKind {
26    Triangle { x0:f32, y0:f32, x1:f32, y1:f32, x2:f32, y2:f32 },
27    /// Gouraud-interpolated + per-pixel posterised triangle (smooth cel).
28    TriangleG { x0:f32, y0:f32, c0:u32, x1:f32, y1:f32, c1:u32, x2:f32, y2:f32, c2:u32, bands:u32 },
29    Line     { x0:f32, y0:f32, x1:f32, y1:f32 },
30}
31
32/// Deferred depth-sorted draw queue.
33#[derive(Default, Debug)]
34pub struct DepthQueue {
35    calls: Vec<DrawCall>,
36}
37
38impl DepthQueue {
39    /// Queue a filled triangle.
40    pub fn push_triangle(
41        &mut self, depth: f32, color: u32,
42        x0:f32, y0:f32, x1:f32, y1:f32, x2:f32, y2:f32,
43    ) {
44        self.calls.push(DrawCall {
45            depth, color,
46            kind: DrawKind::Triangle { x0, y0, x1, y1, x2, y2 },
47        });
48    }
49
50    /// Queue a Gouraud + posterised triangle (smooth cel).
51    #[allow(clippy::too_many_arguments)]
52    pub fn push_triangle_g(
53        &mut self, depth: f32,
54        x0:f32, y0:f32, c0:u32, x1:f32, y1:f32, c1:u32, x2:f32, y2:f32, c2:u32, bands:u32,
55    ) {
56        self.calls.push(DrawCall {
57            depth, color: c0,
58            kind: DrawKind::TriangleG { x0,y0,c0, x1,y1,c1, x2,y2,c2, bands },
59        });
60    }
61
62    /// Queue a line segment.
63    pub fn push_line(
64        &mut self, depth: f32, color: u32,
65        x0:f32, y0:f32, x1:f32, y1:f32,
66    ) {
67        self.calls.push(DrawCall {
68            depth, color,
69            kind: DrawKind::Line { x0, y0, x1, y1 },
70        });
71    }
72
73    /// Sort back-to-front and rasterise everything into `buf`.
74    /// Consumes `self` — call site does `mem::take` to avoid borrow conflict.
75    #[cfg(not(target_arch = "wasm32"))]
76    pub fn flush(mut self, buf: &mut Vec<u32>, width: usize, height: usize) {
77        // Sort largest depth first (furthest → painted first, nearest on top)
78        self.calls.sort_unstable_by(|a, b| {
79            b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal)
80        });
81        for call in &self.calls {
82            match call.kind {
83                DrawKind::Triangle { x0, y0, x1, y1, x2, y2 } =>
84                    raster::fill_triangle(buf, width, height, call.color,
85                                         x0, y0, x1, y1, x2, y2),
86                DrawKind::TriangleG { x0,y0,c0, x1,y1,c1, x2,y2,c2, bands } =>
87                    raster::fill_triangle_gouraud(buf, width, height,
88                                         x0,y0,c0, x1,y1,c1, x2,y2,c2, bands),
89                DrawKind::Line { x0, y0, x1, y1 } =>
90                    raster::draw_line(buf, width, height, call.color,
91                                      x0, y0, x1, y1),
92            }
93        }
94        // `self` dropped here — no need to clear explicitly
95    }
96
97    pub fn is_empty(&self) -> bool { self.calls.is_empty() }
98
99    /// Consume the queue and send all draw calls to the WebGL backend.
100    /// Only compiled for wasm32 targets.
101    #[cfg(target_arch = "wasm32")]
102    pub fn flush_to_webgl(mut self, fill_r: f32, fill_g: f32, fill_b: f32, width: usize, height: usize) {
103        // Sort back-to-front (painter's algorithm) — same as the native path.
104        self.calls.sort_unstable_by(|a, b| {
105            b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal)
106        });
107        for call in &self.calls {
108            match call.kind {
109                DrawKind::Triangle { x0, y0, x1, y1, x2, y2 } =>
110                    crate::gfx::webgl::push_triangle(call.color, x0, y0, x1, y1, x2, y2, call.depth),
111                DrawKind::TriangleG { x0,y0,c0, x1,y1,c1, x2,y2,c2, bands:_ } => {
112                    // WebGL path: approximate with the averaged vertex colour.
113                    let avg = {
114                        let r=((c0>>16&0xFF)+(c1>>16&0xFF)+(c2>>16&0xFF))/3;
115                        let g=((c0>>8&0xFF)+(c1>>8&0xFF)+(c2>>8&0xFF))/3;
116                        let b=((c0&0xFF)+(c1&0xFF)+(c2&0xFF))/3;
117                        (r<<16)|(g<<8)|b
118                    };
119                    crate::gfx::webgl::push_triangle(avg, x0, y0, x1, y1, x2, y2, call.depth);
120                }
121                DrawKind::Line { x0, y0, x1, y1 } =>
122                    crate::gfx::webgl::push_line(call.color, x0, y0, x1, y1, call.depth),
123            }
124        }
125        crate::gfx::webgl::flush(fill_r, fill_g, fill_b, width, height);
126    }
127}