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    Line     { x0:f32, y0:f32, x1:f32, y1:f32 },
28}
29
30/// Deferred depth-sorted draw queue.
31#[derive(Default, Debug)]
32pub struct DepthQueue {
33    calls: Vec<DrawCall>,
34}
35
36impl DepthQueue {
37    /// Queue a filled triangle.
38    pub fn push_triangle(
39        &mut self, depth: f32, color: u32,
40        x0:f32, y0:f32, x1:f32, y1:f32, x2:f32, y2:f32,
41    ) {
42        self.calls.push(DrawCall {
43            depth, color,
44            kind: DrawKind::Triangle { x0, y0, x1, y1, x2, y2 },
45        });
46    }
47
48    /// Queue a line segment.
49    pub fn push_line(
50        &mut self, depth: f32, color: u32,
51        x0:f32, y0:f32, x1:f32, y1:f32,
52    ) {
53        self.calls.push(DrawCall {
54            depth, color,
55            kind: DrawKind::Line { x0, y0, x1, y1 },
56        });
57    }
58
59    /// Sort back-to-front and rasterise everything into `buf`.
60    /// Consumes `self` — call site does `mem::take` to avoid borrow conflict.
61    #[cfg(not(target_arch = "wasm32"))]
62    pub fn flush(mut self, buf: &mut Vec<u32>, width: usize, height: usize) {
63        // Sort largest depth first (furthest → painted first, nearest on top)
64        self.calls.sort_unstable_by(|a, b| {
65            b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal)
66        });
67        for call in &self.calls {
68            match call.kind {
69                DrawKind::Triangle { x0, y0, x1, y1, x2, y2 } =>
70                    raster::fill_triangle(buf, width, height, call.color,
71                                         x0, y0, x1, y1, x2, y2),
72                DrawKind::Line { x0, y0, x1, y1 } =>
73                    raster::draw_line(buf, width, height, call.color,
74                                      x0, y0, x1, y1),
75            }
76        }
77        // `self` dropped here — no need to clear explicitly
78    }
79
80    pub fn is_empty(&self) -> bool { self.calls.is_empty() }
81
82    /// Consume the queue and send all draw calls to the WebGL backend.
83    /// Only compiled for wasm32 targets.
84    #[cfg(target_arch = "wasm32")]
85    pub fn flush_to_webgl(mut self, fill_r: f32, fill_g: f32, fill_b: f32, width: usize, height: usize) {
86        // Sort back-to-front (painter's algorithm) — same as the native path.
87        self.calls.sort_unstable_by(|a, b| {
88            b.depth.partial_cmp(&a.depth).unwrap_or(std::cmp::Ordering::Equal)
89        });
90        for call in &self.calls {
91            match call.kind {
92                DrawKind::Triangle { x0, y0, x1, y1, x2, y2 } =>
93                    crate::gfx::webgl::push_triangle(call.color, x0, y0, x1, y1, x2, y2, call.depth),
94                DrawKind::Line { x0, y0, x1, y1 } =>
95                    crate::gfx::webgl::push_line(call.color, x0, y0, x1, y1, call.depth),
96            }
97        }
98        crate::gfx::webgl::flush(fill_r, fill_g, fill_b, width, height);
99    }
100}