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,
11 pub sry: f32,
12 /// Precomputed cos/sin of the X-axis rotation angle.
13 pub crx: f32,
14 pub srx: f32,
15 /// Screen-centre in pixels (set automatically when the window opens).
16 pub cx: f32,
17 pub cy: f32,
18 /// Focal length in pixels — controls field of view.
19 pub focal: f32,
20 /// Z offset added before the perspective divide (keeps objects in front of
21 /// the camera; typical value 4–6).
22 pub zdist: f32,
23 /// World-space camera position — subtracted from every point before rotation.
24 /// Move the camera with set_camera_pos / move_camera.
25 pub tx: f32,
26 pub ty: f32,
27 pub tz: f32,
28}
29
30impl Default for Camera3D {
31 fn default() -> Self {
32 Self {
33 cry: 1.0,
34 sry: 0.0,
35 crx: 1.0,
36 srx: 0.0,
37 cx: 960.0,
38 cy: 540.0,
39 focal: 1080.0,
40 zdist: 5.0,
41 tx: 0.0,
42 ty: 0.0,
43 tz: 0.0,
44 }
45 }
46}
47
48impl Camera3D {
49 /// Camera-space depth only — cheaper than a full project() when you only
50 /// need to test whether a point is in front of the camera.
51 #[inline]
52 pub fn depth(&self, wx: f32, wy: f32, wz: f32) -> f32 {
53 let wx = wx - self.tx;
54 let wy = wy - self.ty;
55 let wz = wz - self.tz;
56 let rz1 = wx * self.sry + wz * self.cry;
57 wy * self.srx + rz1 * self.crx
58 }
59
60 /// Project a world-space point to (screen_x, screen_y, camera_depth).
61 /// Pipeline: translate → Y-rotation → X-rotation → perspective divide.
62 #[inline]
63 pub fn project(&self, wx: f32, wy: f32, wz: f32) -> (f32, f32, f32) {
64 let wx = wx - self.tx;
65 let wy = wy - self.ty;
66 let wz = wz - self.tz;
67 // — Y rotation —
68 let rx = wx * self.cry - wz * self.sry;
69 let rz1 = wx * self.sry + wz * self.cry;
70 // — X rotation —
71 let ry = wy * self.crx - rz1 * self.srx;
72 let rz = wy * self.srx + rz1 * self.crx;
73 // — Perspective —
74 let d = rz + self.zdist;
75 let sx = self.cx + self.focal * rx / d;
76 let sy = self.cy + self.focal * ry / d;
77 (sx, sy, rz)
78 }
79}