embedded_gfx/
lib.rs

1use std::f32::consts;
2
3use embedded_graphics_core::pixelcolor::Rgb565;
4use embedded_graphics_core::pixelcolor::RgbColor;
5use mesh::K3dMesh;
6use mesh::RenderMode;
7use nalgebra::Isometry3;
8use nalgebra::Matrix4;
9use nalgebra::Perspective3;
10use nalgebra::Point2;
11use nalgebra::Point3;
12use nalgebra::Similarity3;
13use nalgebra::UnitQuaternion;
14use nalgebra::Vector3;
15
16pub mod draw;
17pub mod framebuffer;
18pub mod mesh;
19pub mod perfcounter;
20
21#[derive(Debug)]
22pub enum DrawPrimitive {
23    ColoredPoint(Point2<i32>, Rgb565),
24    Line(Point2<i32>, Point2<i32>, Rgb565),
25    ColoredTriangle(Point2<i32>, Point2<i32>, Point2<i32>, Rgb565),
26}
27
28pub struct K3dCamera {
29    pub position: Point3<f32>,
30    fov: f32,
31    near: f32,
32    far: f32,
33    pub view_matrix: nalgebra::Matrix4<f32>,
34    projection_matrix: nalgebra::Matrix4<f32>,
35    vp_matrix: nalgebra::Matrix4<f32>,
36    target: Point3<f32>,
37    aspect_ratio: f32,
38}
39
40impl K3dCamera {
41    pub fn new(aspect_ratio: f32) -> K3dCamera {
42        let mut ret = K3dCamera {
43            position: Point3::new(0.0, 0.0, 0.0),
44            fov: consts::PI / 2.0,
45            view_matrix: nalgebra::Matrix4::identity(),
46            projection_matrix: nalgebra::Matrix4::identity(),
47            vp_matrix: nalgebra::Matrix4::identity(),
48            target: Point3::new(0.0, 0.0, 0.0),
49            aspect_ratio,
50            near: 0.4,
51            far: 20.0,
52        };
53
54        ret.update_projection();
55
56        ret
57    }
58
59    pub fn set_position(&mut self, pos: Point3<f32>) {
60        self.position = pos;
61
62        self.update_view();
63    }
64
65    pub fn set_fovy(&mut self, fovy: f32) {
66        self.fov = fovy;
67
68        self.update_projection();
69    }
70
71    pub fn set_target(&mut self, target: Point3<f32>) {
72        self.target = target;
73        self.update_view();
74    }
75
76    pub fn get_direction(&self) -> Vector3<f32> {
77        // Vector3::new(
78        //     self.view_matrix[(0, 2)],
79        //     self.view_matrix[(1, 2)],
80        //     self.view_matrix[(2, 2)],
81        // )
82
83        let transpose = self.view_matrix; //.transpose();
84
85        Vector3::new(transpose[(2, 0)], transpose[(2, 1)], transpose[(2, 2)])
86    }
87
88    // pub fn set_attitude(&mut self, roll: f32, pitch: f32, yaw: f32) {
89    //     let rotation = UnitQuaternion::from_euler_angles(roll, pitch, yaw);
90    //     let translation = self.position;
91    //     let isometry = Isometry3::from_parts(translation.coords.into(), rotation);
92    //     self.view_matrix = isometry.to_homogeneous();
93    // }
94
95    fn update_view(&mut self) {
96        let view = Isometry3::look_at_rh(&self.position, &self.target, &Vector3::y());
97
98        self.view_matrix = view.to_homogeneous();
99        self.vp_matrix = self.projection_matrix * self.view_matrix;
100    }
101
102    fn update_projection(&mut self) {
103        let projection = Perspective3::new(self.aspect_ratio, self.fov, self.near, self.far);
104        self.projection_matrix = projection.to_homogeneous();
105        self.vp_matrix = self.projection_matrix * self.view_matrix;
106    }
107}
108
109pub struct K3dengine {
110    pub camera: K3dCamera,
111    width: u16,
112    height: u16,
113}
114
115impl K3dengine {
116    pub fn new(width: u16, height: u16) -> K3dengine {
117        K3dengine {
118            camera: K3dCamera::new(width as f32 / height as f32),
119            width,
120            height,
121        }
122    }
123
124    fn transform_point(
125        &self,
126        point: &(f32, f32, f32),
127        model_matrix: Matrix4<f32>,
128    ) -> Option<Point3<i32>> {
129        let point = nalgebra::Vector4::new(point.0, point.1, point.2, 1.0);
130        let point = model_matrix * point;
131
132        if point.w < 0.0 {
133            return None;
134        }
135        if point.z < self.camera.near || point.z > self.camera.far {
136            return None;
137        }
138
139        let point = Point3::from_homogeneous(point)?;
140
141        Some(Point3::new(
142            ((1.0 + point.x) * 0.5 * self.width as f32) as i32,
143            ((1.0 - point.y) * 0.5 * self.height as f32) as i32,
144            (point.z * (self.camera.far - self.camera.near) + self.camera.near) as i32,
145        ))
146    }
147
148    pub fn render<'a, MS, F>(&self, meshes: MS, mut callback: F)
149    where
150        MS: IntoIterator<Item = &'a K3dMesh<'a>>,
151        F: FnMut(DrawPrimitive),
152    {
153        for mesh in meshes {
154            if mesh.geometry.vertices.is_empty() {
155                continue;
156            }
157
158            let transform_matrix = self.camera.vp_matrix * mesh.model_matrix;
159            let screen_space_points = mesh
160                .geometry
161                .vertices
162                .iter()
163                .filter_map(|v| self.transform_point(v, transform_matrix));
164
165            match mesh.render_mode {
166                RenderMode::Points
167                    if mesh.geometry.colors.len() == mesh.geometry.vertices.len() =>
168                {
169                    for (point, color) in screen_space_points.zip(mesh.geometry.colors) {
170                        callback(DrawPrimitive::ColoredPoint(point.xy(), *color));
171                    }
172                }
173
174                RenderMode::Points => {
175                    for point in screen_space_points {
176                        callback(DrawPrimitive::ColoredPoint(point.xy(), mesh.color));
177                    }
178                }
179
180                RenderMode::Lines if !mesh.geometry.lines.is_empty() => {
181                    for line in mesh.geometry.lines {
182                        let p1 =
183                            self.transform_point(&mesh.geometry.vertices[line.0], transform_matrix);
184                        let p2 =
185                            self.transform_point(&mesh.geometry.vertices[line.1], transform_matrix);
186
187                        if let (Some(p1), Some(p2)) = (p1, p2) {
188                            callback(DrawPrimitive::Line(p1.xy(), p2.xy(), mesh.color));
189                        }
190                    }
191                }
192
193                RenderMode::Lines if !mesh.geometry.faces.is_empty() => {
194                    for face in mesh.geometry.faces {
195                        let p1 =
196                            self.transform_point(&mesh.geometry.vertices[face.0], transform_matrix);
197                        let p2 =
198                            self.transform_point(&mesh.geometry.vertices[face.1], transform_matrix);
199                        let p3 =
200                            self.transform_point(&mesh.geometry.vertices[face.2], transform_matrix);
201
202                        if let (Some(p1), Some(p2), Some(p3)) = (p1, p2, p3) {
203                            callback(DrawPrimitive::Line(p1.xy(), p2.xy(), mesh.color));
204                            callback(DrawPrimitive::Line(p2.xy(), p3.xy(), mesh.color));
205                            callback(DrawPrimitive::Line(p3.xy(), p1.xy(), mesh.color));
206                        }
207                    }
208                }
209
210                RenderMode::Lines => {}
211
212                RenderMode::SolidLightDir(direction) => {
213                    for (face, normal) in mesh.geometry.faces.iter().zip(mesh.geometry.normals) {
214                        //Backface culling
215                        let normal = Vector3::new(normal.0, normal.1, normal.2);
216
217                        let transformed_normal = mesh.model_matrix.transform_vector(&normal);
218
219                        if self.camera.get_direction().dot(&transformed_normal) < 0.0 {
220                            continue;
221                        }
222
223                        let p1 =
224                            self.transform_point(&mesh.geometry.vertices[face.0], transform_matrix);
225                        let p2 =
226                            self.transform_point(&mesh.geometry.vertices[face.1], transform_matrix);
227                        let p3 =
228                            self.transform_point(&mesh.geometry.vertices[face.2], transform_matrix);
229
230                        if let (Some(p1), Some(p2), Some(p3)) = (p1, p2, p3) {
231                            let color_as_float = Vector3::new(
232                                mesh.color.r() as f32 / 32.0,
233                                mesh.color.g() as f32 / 64.0,
234                                mesh.color.b() as f32 / 32.0,
235                            );
236
237                            let mut final_color = Vector3::new(0.0f32, 0.0, 0.0);
238
239                            let intensity = transformed_normal.dot(&direction);
240
241                            let intensity = intensity.max(0.0);
242
243                            final_color += color_as_float * intensity + color_as_float * 0.4;
244
245                            let final_color = Vector3::new(
246                                final_color.x.min(1.0).max(0.0),
247                                final_color.y.min(1.0).max(0.0),
248                                final_color.z.min(1.0).max(0.0),
249                            );
250
251                            let color = Rgb565::new(
252                                (final_color.x * 31.0) as u8,
253                                (final_color.y * 63.0) as u8,
254                                (final_color.z * 31.0) as u8,
255                            );
256                            callback(DrawPrimitive::ColoredTriangle(
257                                p1.xy(),
258                                p2.xy(),
259                                p3.xy(),
260                                color,
261                            ));
262                        }
263                    }
264                }
265
266                RenderMode::Solid => {
267                    if mesh.geometry.normals.is_empty() {
268                        for face in mesh.geometry.faces.iter() {
269                            let p1 = self
270                                .transform_point(&mesh.geometry.vertices[face.0], transform_matrix);
271                            let p2 = self
272                                .transform_point(&mesh.geometry.vertices[face.1], transform_matrix);
273                            let p3 = self
274                                .transform_point(&mesh.geometry.vertices[face.2], transform_matrix);
275
276                            if let (Some(p1), Some(p2), Some(p3)) = (p1, p2, p3) {
277                                callback(DrawPrimitive::ColoredTriangle(
278                                    p1.xy(),
279                                    p2.xy(),
280                                    p3.xy(),
281                                    mesh.color,
282                                ));
283                            }
284                        }
285                    } else {
286                        for (face, normal) in mesh.geometry.faces.iter().zip(mesh.geometry.normals)
287                        {
288                            //Backface culling
289                            let normal = Vector3::new(normal.0, normal.1, normal.2);
290
291                            let transformed_normal = mesh.model_matrix.transform_vector(&normal);
292
293                            if self.camera.get_direction().dot(&transformed_normal) < 0.0 {
294                                continue;
295                            }
296
297                            let p1 = self
298                                .transform_point(&mesh.geometry.vertices[face.0], transform_matrix);
299                            let p2 = self
300                                .transform_point(&mesh.geometry.vertices[face.1], transform_matrix);
301                            let p3 = self
302                                .transform_point(&mesh.geometry.vertices[face.2], transform_matrix);
303
304                            if let (Some(p1), Some(p2), Some(p3)) = (p1, p2, p3) {
305                                callback(DrawPrimitive::ColoredTriangle(
306                                    p1.xy(),
307                                    p2.xy(),
308                                    p3.xy(),
309                                    mesh.color,
310                                ));
311                            }
312                        }
313                    }
314                }
315            }
316        }
317    }
318}