1extern crate std;
20
21use crate::mesh::{K3dMesh, RenderMode};
22use crate::{DrawPrimitive, K3dengine};
23use core::cmp::Ordering;
24use embedded_graphics_core::pixelcolor::{Rgb565, RgbColor};
25use std::vec::Vec;
26
27#[derive(Debug, Clone)]
29pub struct DepthSortedTriangle {
30 pub primitive: DrawPrimitive,
31 pub avg_depth: f32,
32}
33
34impl DepthSortedTriangle {
35 pub fn new(primitive: DrawPrimitive, avg_depth: f32) -> Self {
37 Self {
38 primitive,
39 avg_depth,
40 }
41 }
42}
43
44impl K3dengine {
45 pub fn render_painters_algorithm<'a, MS, F>(
58 &self,
59 meshes: MS,
60 triangles: &mut Vec<DepthSortedTriangle>,
61 mut callback: F,
62 ) -> usize
63 where
64 MS: IntoIterator<Item = &'a K3dMesh<'a>>,
65 F: FnMut(DrawPrimitive),
66 {
67 triangles.clear();
68
69 for mesh in meshes {
71 if mesh.geometry.vertices.is_empty() {
72 continue;
73 }
74
75 if self.should_cull_mesh(mesh) {
77 continue;
78 }
79
80 let mesh_pos = mesh.get_position();
82 let distance = (mesh_pos - self.camera.position).norm();
83 let geometry = mesh.select_lod(distance);
84
85 let transform_matrix = self.camera.vp_matrix * mesh.model_matrix;
86
87 match mesh.render_mode {
89 RenderMode::Solid
90 | RenderMode::SolidLightDir(_)
91 | RenderMode::BlinnPhong { .. } => {
92 for face in geometry.faces {
93 if let Some([p1, p2, p3]) =
94 self.transform_points(face, geometry.vertices, transform_matrix)
95 {
96 let v1 = (p2.x - p1.x, p2.y - p1.y);
98 let v2 = (p3.x - p1.x, p3.y - p1.y);
99 let cross = v1.0 * v2.1 - v1.1 * v2.0;
100 if cross <= 0 {
101 continue;
102 }
103
104 let avg_depth = (p1.z + p2.z + p3.z) as f32 / 3.0;
106
107 let color = match mesh.render_mode {
109 RenderMode::SolidLightDir(light_dir) => self.calculate_lit_color(
110 face,
111 geometry.vertices,
112 geometry.normals,
113 mesh.color,
114 light_dir,
115 ),
116 RenderMode::BlinnPhong { .. } => {
117 mesh.color
120 }
121 _ => mesh.color,
122 };
123
124 let primitive =
125 DrawPrimitive::ColoredTriangle([p1.xy(), p2.xy(), p3.xy()], color);
126
127 triangles.push(DepthSortedTriangle::new(primitive, avg_depth));
128 }
129 }
130 }
131 _ => {}
133 }
134 }
135
136 triangles.sort_by(|a, b| {
138 b.avg_depth
139 .partial_cmp(&a.avg_depth)
140 .unwrap_or(Ordering::Equal)
141 });
142
143 let count = triangles.len();
145 for triangle in triangles.iter() {
146 callback(triangle.primitive.clone());
147 }
148
149 count
150 }
151
152 #[allow(dead_code)]
154 fn calculate_lit_color(
155 &self,
156 face: &[usize; 3],
157 vertices: &[[f32; 3]],
158 normals: &[[f32; 3]],
159 base_color: Rgb565,
160 light_dir: nalgebra::Vector3<f32>,
161 ) -> Rgb565 {
162 let normal = if !normals.is_empty() && face[0] < normals.len() {
164 nalgebra::Vector3::new(
165 normals[face[0]][0],
166 normals[face[0]][1],
167 normals[face[0]][2],
168 )
169 } else {
170 let v0 = &vertices[face[0]];
172 let v1 = &vertices[face[1]];
173 let v2 = &vertices[face[2]];
174
175 let edge1 = nalgebra::Vector3::new(v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]);
176 let edge2 = nalgebra::Vector3::new(v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]);
177
178 let normal = edge1.cross(&edge2);
179 if normal.norm() > 0.0 {
180 normal.normalize()
181 } else {
182 nalgebra::Vector3::new(0.0, 1.0, 0.0)
183 }
184 };
185
186 let light_intensity = normal.dot(&light_dir.normalize()).max(0.0);
188 let ambient = 0.3;
189 let final_intensity = (ambient + (1.0 - ambient) * light_intensity).clamp(0.0, 1.0);
190
191 let r = (base_color.r() as f32 * final_intensity) as u8;
193 let g = (base_color.g() as f32 * final_intensity) as u8;
194 let b = (base_color.b() as f32 * final_intensity) as u8;
195
196 Rgb565::new(r, g, b)
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 extern crate std;
203 use super::*;
204 use embedded_graphics_core::pixelcolor::Rgb565;
205 use std::cmp::Ordering;
206
207 #[test]
208 fn test_sorting_by_depth() {
209 let mut triangles = std::vec![
210 DepthSortedTriangle {
211 primitive: DrawPrimitive::Line(
212 [nalgebra::Point2::new(0, 0), nalgebra::Point2::new(1, 1)],
213 Rgb565::new(31, 0, 0),
214 ),
215 avg_depth: 10.0,
216 },
217 DepthSortedTriangle {
218 primitive: DrawPrimitive::Line(
219 [nalgebra::Point2::new(0, 0), nalgebra::Point2::new(1, 1)],
220 Rgb565::new(0, 63, 0),
221 ),
222 avg_depth: 5.0,
223 },
224 DepthSortedTriangle {
225 primitive: DrawPrimitive::Line(
226 [nalgebra::Point2::new(0, 0), nalgebra::Point2::new(1, 1)],
227 Rgb565::new(0, 0, 31),
228 ),
229 avg_depth: 15.0,
230 },
231 ];
232
233 triangles.sort_by(|a, b| {
234 b.avg_depth
235 .partial_cmp(&a.avg_depth)
236 .unwrap_or(Ordering::Equal)
237 });
238
239 assert_eq!(triangles[0].avg_depth, 15.0);
241 assert_eq!(triangles[1].avg_depth, 10.0);
242 assert_eq!(triangles[2].avg_depth, 5.0);
243 }
244}