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}