Skip to main content

agpu/scene3d/
scene.rs

1//! Scene graph — retained-mode 3D scene with nodes, camera, and lights.
2
3use super::camera::Camera;
4use super::light::Light;
5use super::mesh::Mesh;
6
7/// A node in the 3D scene graph.
8pub struct SceneNode {
9    pub mesh: Mesh,
10    pub translation: [f32; 3],
11    pub rotation: [f32; 3], // Euler angles in radians
12    pub scale_factor: [f32; 3],
13    pub visible: bool,
14}
15
16impl SceneNode {
17    pub fn mesh(mesh: Mesh) -> Self {
18        Self {
19            mesh,
20            translation: [0.0, 0.0, 0.0],
21            rotation: [0.0, 0.0, 0.0],
22            scale_factor: [1.0, 1.0, 1.0],
23            visible: true,
24        }
25    }
26
27    pub fn translate(mut self, translation: [f32; 3]) -> Self {
28        self.translation = translation;
29        self
30    }
31
32    pub fn rotate(mut self, rotation: [f32; 3]) -> Self {
33        self.rotation = rotation;
34        self
35    }
36
37    pub fn scale(mut self, scale: [f32; 3]) -> Self {
38        self.scale_factor = scale;
39        self
40    }
41
42    pub fn visible(mut self, visible: bool) -> Self {
43        self.visible = visible;
44        self
45    }
46
47    /// Compute a 4×4 model matrix (column-major) from translation, rotation, scale.
48    pub fn model_matrix(&self) -> [f32; 16] {
49        let [tx, ty, tz] = self.translation;
50        let [rx, ry, rz] = self.rotation;
51        let [sx, sy, sz] = self.scale_factor;
52
53        // Rotation: Rz * Ry * Rx
54        let (cx, sx_r) = (rx.cos(), rx.sin());
55        let (cy, sy_r) = (ry.cos(), ry.sin());
56        let (cz, sz_r) = (rz.cos(), rz.sin());
57
58        [
59            sx * cy * cz,
60            sx * cy * sz_r,
61            sx * (-sy_r),
62            0.0,
63            sy * (sx_r * sy_r * cz - cx * sz_r),
64            sy * (sx_r * sy_r * sz_r + cx * cz),
65            sy * sx_r * cy,
66            0.0,
67            sz * (cx * sy_r * cz + sx_r * sz_r),
68            sz * (cx * sy_r * sz_r - sx_r * cz),
69            sz * cx * cy,
70            0.0,
71            tx,
72            ty,
73            tz,
74            1.0,
75        ]
76    }
77}
78
79/// A complete 3D scene with camera, lights, and nodes.
80pub struct Scene3D {
81    camera: Camera,
82    lights: Vec<Light>,
83    nodes: Vec<SceneNode>,
84}
85
86impl Scene3D {
87    pub fn new() -> Self {
88        Self {
89            camera: Camera::perspective(60.0, 16.0 / 9.0, 0.1, 100.0),
90            lights: Vec::new(),
91            nodes: Vec::new(),
92        }
93    }
94
95    pub fn set_camera(&mut self, camera: Camera) {
96        self.camera = camera;
97    }
98
99    pub fn camera(&self) -> &Camera {
100        &self.camera
101    }
102
103    pub fn add_light(&mut self, light: Light) {
104        self.lights.push(light);
105    }
106
107    pub fn lights(&self) -> &[Light] {
108        &self.lights
109    }
110
111    pub fn add(&mut self, node: SceneNode) {
112        self.nodes.push(node);
113    }
114
115    pub fn nodes(&self) -> &[SceneNode] {
116        &self.nodes
117    }
118
119    pub fn nodes_mut(&mut self) -> &mut [SceneNode] {
120        &mut self.nodes
121    }
122}
123
124impl Default for Scene3D {
125    fn default() -> Self {
126        Self::new()
127    }
128}