Skip to main content

ling/gfx/
camera.rs

1// src/gfx/camera.rs — 3-D camera: Y-then-X rotation + perspective projection.
2//
3// The camera is stored in GfxState and used by the 3-D draw builtins
4// (`วาดสามเหลี่ยม3มิติ`, `วาดเส้น3มิติ`).  Ling programs call `set_camera`
5// once per frame after computing their trig values.
6
7#[derive(Debug, Clone)]
8pub struct Camera3D {
9    /// Precomputed cos/sin of the Y-axis rotation angle.
10    pub cry: f32, pub sry: f32,
11    /// Precomputed cos/sin of the X-axis rotation angle.
12    pub crx: f32, pub srx: f32,
13    /// Screen-centre in pixels (set automatically when the window opens).
14    pub cx:  f32, pub cy:  f32,
15    /// Focal length in pixels — controls field of view.
16    pub focal: f32,
17    /// Z offset added before the perspective divide (keeps objects in front of
18    /// the camera; typical value 4–6).
19    pub zdist: f32,
20    /// World-space camera position — subtracted from every point before rotation.
21    /// Move the camera with set_camera_pos / move_camera.
22    pub tx: f32, pub ty: f32, pub tz: f32,
23}
24
25impl Default for Camera3D {
26    fn default() -> Self {
27        Self {
28            cry: 1.0, sry: 0.0,
29            crx: 1.0, srx: 0.0,
30            cx:  960.0, cy: 540.0,
31            focal: 1080.0,
32            zdist: 5.0,
33            tx: 0.0, ty: 0.0, tz: 0.0,
34        }
35    }
36}
37
38impl Camera3D {
39    /// Camera-space depth only — cheaper than a full project() when you only
40    /// need to test whether a point is in front of the camera.
41    #[inline]
42    pub fn depth(&self, wx: f32, wy: f32, wz: f32) -> f32 {
43        let wx = wx - self.tx;
44        let wy = wy - self.ty;
45        let wz = wz - self.tz;
46        let rz1 = wx * self.sry + wz * self.cry;
47        let rz  = wy * self.srx + rz1 * self.crx;
48        rz
49    }
50
51    /// Project a world-space point to (screen_x, screen_y, camera_depth).
52    /// Pipeline: translate → Y-rotation → X-rotation → perspective divide.
53    #[inline]
54    pub fn project(&self, wx: f32, wy: f32, wz: f32) -> (f32, f32, f32) {
55        let wx = wx - self.tx;
56        let wy = wy - self.ty;
57        let wz = wz - self.tz;
58        // — Y rotation —
59        let rx  =  wx * self.cry - wz * self.sry;
60        let rz1 =  wx * self.sry + wz * self.cry;
61        // — X rotation —
62        let ry  =  wy * self.crx - rz1 * self.srx;
63        let rz  =  wy * self.srx + rz1 * self.crx;
64        // — Perspective —
65        let d   = rz + self.zdist;
66        let sx  = self.cx    + self.focal * rx / d;
67        let sy  = self.cy    + self.focal * ry / d;
68        (sx, sy, rz)
69    }
70}