cube_core/game/
render.rs

1use std::io::{self, Write};
2
3use crate::{
4    utils::{
5        cube_utils::Color,
6        geometry::{Point2D, Point3D, Triangle}
7    },
8    cube::{
9        slice::FaceSlice,
10        cube::Face
11    }
12};
13
14const PRINT_CHAR: &str = "██";
15const ANSI_RESET: &str = "\x1b[0m";
16
17pub enum AnyFace {
18    Face(Face),
19    FaceSlice(FaceSlice),
20}
21
22impl AnyFace {
23    pub fn avg_z(&self) -> f32 {
24        match self {
25            AnyFace::Face(f) => f.avg_z(),
26            AnyFace::FaceSlice(fs) => fs.avg_z(),
27        }
28    }
29}
30
31pub trait Renderable {
32    fn get_visible_faces(&self) -> Vec<AnyFace>;
33
34    fn dist(&self) -> f32;
35}
36
37pub struct Screen {
38    size_x: usize,
39    size_y: usize,
40    screen: Vec<Vec<Option<Color>>>,
41    zp: f32,
42    projection_scale: f32,
43}
44
45impl Screen {
46    pub fn new(size_x: usize, size_y: usize, zp: f32, projection_scale: f32) -> Screen {
47        Screen {
48            size_x,
49            size_y,
50            screen: vec![vec![None; size_x]; size_y],
51            zp,
52            projection_scale,
53        }
54    }
55
56    pub fn color_at(&self, x: i16, y: i16) -> Option<Color> {
57        self.screen[self.size_y - y as usize - 1][x as usize]
58    }
59
60    fn project_point(&self, p: Point3D) -> Point2D {
61        let screen_x_offset = (self.size_x / 2) as isize;
62        let screen_y_offset = (self.size_y / 2) as isize;
63
64        let multiplier = self.zp / p.z;
65        let xp = p.x * multiplier;
66        let yp = p.y * multiplier;
67
68        let x_proj = (xp * self.projection_scale) as isize + screen_x_offset;
69        let y_proj = (yp * self.projection_scale) as isize + screen_y_offset;
70
71        Point2D {x: x_proj, y: y_proj}
72    }
73
74    // uses the "scanline" method
75    fn rasterize_triangle(&mut self, tri: Triangle, color: Color) {
76        let mut pts = [tri.0, tri.1, tri.2];
77        // sort vertices by y (top to bottom)
78        pts.sort_by_key(|p| p.y);
79
80        let (v1, v2, v3) = (pts[0], pts[1], pts[2]);
81
82        // avoid dividing by zero
83        let inv_slope_1 = if v2.y != v1.y {
84            (v2.x - v1.x) as f32 / (v2.y - v1.y) as f32
85        } else { 0.0 };
86
87        let inv_slope_2 = if v3.y != v1.y {
88            (v3.x - v1.x) as f32 / (v3.y - v1.y) as f32
89        } else { 0.0 };
90
91        let inv_slope_3 = if v3.y != v2.y {
92            (v3.x - v2.x) as f32 / (v3.y - v2.y) as f32
93        } else { 0.0 };
94
95        // top half (v1 → v2 and v1 → v3)
96        let mut curx1 = v1.x as f32;
97        let mut curx2 = v1.x as f32;
98        for y in v1.y..=v2.y {
99            if y >= 0 && y < self.size_y as isize {
100                let x_start = curx1.min(curx2).max(0.0) as isize;
101                let x_end   = curx1.max(curx2).min(self.size_x as f32 - 1.0) as isize;
102                for x in x_start..=x_end {
103                    self.screen[y as usize][x as usize] = Some(color);
104                }
105            }
106            curx1 += inv_slope_1;
107            curx2 += inv_slope_2;
108        }
109
110        // bottom half (v2 → v3 and v1 → v3)
111        let mut curx1 = v2.x as f32;
112        let mut curx2 = v1.x as f32 + inv_slope_2 * (v2.y - v1.y) as f32;
113        for y in v2.y..=v3.y {
114            if y >= 0 && y < self.size_y as isize {
115                let x_start = curx1.min(curx2).max(0.0) as isize;
116                let x_end   = curx1.max(curx2).min(self.size_x as f32 - 1.0) as isize;
117                for x in x_start..=x_end {
118                    self.screen[y as usize][x as usize] = Some(color);
119                }
120            }
121            curx1 += inv_slope_3;
122            curx2 += inv_slope_2;
123        }
124    }
125
126
127    fn render_face(&mut self, face: &Face) {
128        let projected_markers: Vec<Point2D> = face.markers
129            .iter()
130            .map(|&p| self.project_point(p))
131            .collect();
132
133        for row in 0..3 {
134            for col in 0..3 {
135                let tris = [
136                    Triangle(projected_markers.get(row * 4 + col).unwrap().clone(), 
137                        projected_markers.get(row * 4 + col + 1).unwrap().clone(), 
138                        projected_markers.get((row + 1) * 4 + col + 1).unwrap().clone()
139                    ),
140                    Triangle(projected_markers.get(row * 4 + col).unwrap().clone(), 
141                        projected_markers.get((row + 1) * 4 + col + 1).unwrap().clone(),
142                        projected_markers.get((row + 1) * 4 + col).unwrap().clone()
143                    ),
144                ];
145                let color = face.grid_face.grid[row][col];
146                for tri in tris {
147                    self.rasterize_triangle(tri, color);
148                }
149            }
150        }
151    }
152
153    fn render_face_slice(&mut self, face_slice: &FaceSlice) {
154        let projected_markers: Vec<Point2D> = face_slice.markers
155            .iter()
156            .map(|&p| self.project_point(p))
157            .collect();
158
159        for row in 0..3 {
160            let tris = [
161                Triangle(projected_markers.get(row * 2).unwrap().clone(), 
162                    projected_markers.get(row * 2 + 1).unwrap().clone(), 
163                    projected_markers.get((row + 1) * 2 + 1).unwrap().clone()
164                ),
165                Triangle(projected_markers.get(row * 2).unwrap().clone(), 
166                    projected_markers.get((row + 1) * 2 + 1).unwrap().clone(),
167                    projected_markers.get((row + 1) * 2).unwrap().clone()
168                ),
169            ];
170            let color = face_slice.colors[row];
171            for tri in tris {
172                self.rasterize_triangle(tri, color);
173            }
174        }
175    }
176
177    pub fn render(&mut self, mut renderables: Vec<&dyn Renderable>) {
178        renderables.sort_by(|a, b| a.dist().partial_cmp(&b.dist()).unwrap());
179
180        for renderable in renderables.into_iter().rev() {
181            let faces = renderable.get_visible_faces();
182            for face in faces.into_iter().take(3).rev() {
183                match face {
184                    AnyFace::Face(f) => self.render_face(&f),
185                    AnyFace::FaceSlice(fs) => self.render_face_slice(&fs),
186                };
187            }
188        }
189    }
190
191    pub fn print_screen(&self) {
192        for y in (0..(self.size_y)).rev() {
193            for x in 0..(self.size_x) {
194                match self.screen[y][x] {
195                    Some(color) => print!("{}{}{}", color.to_ansi(), PRINT_CHAR, ANSI_RESET),
196                    _ => print!("  ")
197                };
198            }
199            println!();
200        }
201        io::stdout().flush().unwrap();
202    }
203
204    pub fn reset_terminal() {
205        print!("{esc}c", esc = 27 as char);
206        io::stdout().flush().unwrap();
207    }
208
209    pub fn clear_screen(&mut self) {
210        for row in self.screen.iter_mut() {
211            for cell in row.iter_mut() {
212                *cell = None;
213            }
214        }
215    }
216}