Skip to main content

ling/gfx/
mod.rs

1// src/gfx/mod.rs — unified graphics state + sub-modules.
2//
3// Sub-modules
4//   raster  — pixel-level fill_triangle / draw_line  (native only)
5//   camera  — Camera3D: rotation storage + world→screen projection
6//   light   — Light struct + cel-shading quantiser
7//   depth   — DepthQueue: deferred draw accumulator
8//   vtex    — vector texture primitives
9//   webgl   — WebGL2 backend (wasm32 only)
10
11#[cfg(not(target_arch = "wasm32"))]
12pub mod raster;
13pub mod camera;
14pub mod light;
15pub mod depth;
16pub mod vtex;
17pub mod shapes;
18#[cfg(target_arch = "wasm32")]
19pub mod webgl;
20#[cfg(target_arch = "wasm32")]
21pub mod audio_web;
22
23pub use camera::Camera3D;
24pub use light::Light;
25pub use depth::DepthQueue;
26
27// ─── Native GfxState (minifb window + software framebuffer) ──────────────────
28
29#[cfg(not(target_arch = "wasm32"))]
30pub struct GfxState {
31    pub window:      Option<minifb::Window>,
32    pub buffer:      Vec<u32>,
33    pub width:       usize,
34    pub height:      usize,
35    /// Current pen colour (0x00RRGGBB) set by `สีดินสอ` / `set_color`.
36    pub color:       u32,
37    /// 3-D camera — set once per frame with `set_camera`.
38    pub camera:      Camera3D,
39    /// Active point lights for this frame — cleared by `clear_lights`.
40    pub lights:      Vec<Light>,
41    /// Ambient fill level [0..1].  Default 0.15.
42    pub ambient:     f32,
43    /// Depth-sorted draw queue — flushed by `แสดงผล` / `present`.
44    pub depth_queue: DepthQueue,
45    /// Mouse position delta since last frame (pixels).
46    pub mouse_dx: f32, pub mouse_dy: f32,
47    /// Previous mouse position for delta computation; NaN = no prior sample.
48    pub last_mx: f32, pub last_my: f32,
49    /// When true: cursor is hidden and reset to center every frame for infinite rotation.
50    pub mouse_captured: bool,
51    /// Shading mode for 3-D shape meshes: 0 flat · 1 cel · 2 holo (default).
52    pub shade_mode: u8,
53    /// Tunable cel/holo parameters (bands, shadow tint, rim, …).
54    pub shade: ling_graphics::shading::ShadeParams,
55    /// Blend mode for pixel writes: 0 = normal (overwrite), 1 = additive.
56    pub blend: u8,
57    /// Distance fog: triangles/lines fade toward `fog_color` from `fog_start`
58    /// to `fog_end` (camera-space depth). `fog_end <= 0` disables fog.
59    pub fog_color: u32,
60    pub fog_start: f32,
61    pub fog_end:   f32,
62}
63
64#[cfg(not(target_arch = "wasm32"))]
65impl GfxState {
66    pub fn new() -> Self {
67        Self {
68            window:      None,
69            buffer:      Vec::new(),
70            width:       0,
71            height:      0,
72            color:       0x00FF_FFFF,
73            camera:      Camera3D::default(),
74            lights:      Vec::new(),
75            ambient:     0.15,
76            depth_queue: DepthQueue::default(),
77            mouse_dx:       0.0,
78            mouse_dy:       0.0,
79            last_mx:        f32::NAN,
80            last_my:        f32::NAN,
81            mouse_captured: false,
82            shade_mode:     2, // holographic cel by default
83            shade:          ling_graphics::shading::ShadeParams::default(),
84            blend:          0, // normal (overwrite) by default
85            fog_color:   0x0000_0000,
86            fog_start:   0.0,
87            fog_end:     0.0, // fog off by default
88        }
89    }
90
91    /// Blend a colour toward the fog colour by camera-space `depth`.
92    #[inline]
93    pub fn fog_apply(&self, color: u32, depth: f32) -> u32 {
94        if self.fog_end <= 0.0 { return color; }
95        let span = self.fog_end - self.fog_start;
96        if span <= 0.0 { return color; }
97        let f = ((depth - self.fog_start) / span).clamp(0.0, 1.0);
98        if f <= 0.0 { return color; }
99        let lerp = |a: u32, b: u32| -> u32 {
100            (a as f32 + (b as f32 - a as f32) * f) as u32 & 0xff
101        };
102        let r = lerp((color >> 16) & 0xff, (self.fog_color >> 16) & 0xff);
103        let g = lerp((color >> 8) & 0xff,  (self.fog_color >> 8) & 0xff);
104        let b = lerp(color & 0xff,         self.fog_color & 0xff);
105        (r << 16) | (g << 8) | b
106    }
107
108    pub fn sync_projection(&mut self) {
109        self.camera.cx    = self.width  as f32 / 2.0;
110        self.camera.cy    = self.height as f32 / 2.0;
111        self.camera.focal = self.height as f32;
112        self.camera.zdist = 5.0;
113    }
114}
115
116// ─── WASM GfxState (no window, no software framebuffer) ──────────────────────
117
118#[cfg(target_arch = "wasm32")]
119pub struct GfxState {
120    pub width:       usize,
121    pub height:      usize,
122    /// Current pen colour (0x00RRGGBB).
123    pub color:       u32,
124    /// Fill / clear colour components [0..1].
125    pub fill_r:      f32,
126    pub fill_g:      f32,
127    pub fill_b:      f32,
128    pub camera:      Camera3D,
129    pub lights:      Vec<Light>,
130    pub ambient:     f32,
131    /// Accumulates projected screen-space draw calls; flushed to WebGL by present().
132    pub depth_queue: DepthQueue,
133    pub shade_mode:  u8,
134    pub shade:       ling_graphics::shading::ShadeParams,
135}
136
137#[cfg(target_arch = "wasm32")]
138impl GfxState {
139    pub fn new() -> Self {
140        Self {
141            width:       800,
142            height:      600,
143            color:       0x00FF_FFFF,
144            fill_r:      0.0,
145            fill_g:      0.0,
146            fill_b:      0.0,
147            camera:      Camera3D::default(),
148            lights:      Vec::new(),
149            ambient:     0.15,
150            depth_queue: DepthQueue::default(),
151            shade_mode:  2,
152            shade:       ling_graphics::shading::ShadeParams::default(),
153        }
154    }
155
156    pub fn sync_projection(&mut self) {
157        self.camera.cx    = self.width  as f32 / 2.0;
158        self.camera.cy    = self.height as f32 / 2.0;
159        self.camera.focal = self.height as f32;
160        self.camera.zdist = 5.0;
161    }
162}