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
14pub const SCREEN_X: usize = 320;
15pub const SCREEN_Y: usize = 320;
16
17const SCREEN_X_OFFSET: isize = (SCREEN_X / 2) as isize;
18const SCREEN_Y_OFFSET: isize = (SCREEN_Y / 2) as isize;
19
20const PRINT_CHAR: &str = "██";
21const ANSI_RESET: &str = "\x1b[0m";
22
23pub enum AnyFace {
24    Face(Face),
25    FaceSlice(FaceSlice),
26}
27
28impl AnyFace {
29    pub fn avg_z(&self) -> f32 {
30        match self {
31            AnyFace::Face(f) => f.avg_z(),
32            AnyFace::FaceSlice(fs) => fs.avg_z(),
33        }
34    }
35}
36
37pub trait Renderable {
38    fn get_visible_faces(&self) -> Vec<AnyFace>;
39
40    fn dist(&self) -> f32;
41}
42
43pub struct Screen {
44    screen: Box<[[Option<Color>; SCREEN_X]; SCREEN_Y]>,
45    zp: f32,
46    projection_scale: f32,
47}
48
49impl Screen {
50    pub fn new(zp: f32, projection_scale: f32) -> Screen {
51        Screen {
52            screen: Box::new([[None; SCREEN_X]; SCREEN_Y]),
53            zp,
54            projection_scale
55        }
56    }
57
58    pub fn color_at(&self, x: i16, y: i16) -> Option<Color> {
59        self.screen[SCREEN_Y - y as usize - 1][x as usize]
60    }
61
62    fn project_point(&self, p: Point3D) -> Point2D {
63        let multiplier = self.zp / p.z;
64        let xp = p.x * multiplier;
65        let yp = p.y * multiplier;
66
67        let x_proj = (xp * self.projection_scale) as isize + SCREEN_X_OFFSET;
68        let y_proj = (yp * self.projection_scale) as isize + SCREEN_Y_OFFSET;
69
70        Point2D {x: x_proj, y: y_proj}
71    }
72
73    fn rasterize_triangle(&mut self, triangle: Triangle, color: Color) {
74        let Triangle(a, b, c) = triangle;
75        let min_x = a.x.min(b.x.min(c.x)).max(0);
76        let max_x = a.x.max(b.x.max(c.x)).min(SCREEN_X as isize - 1);
77        let min_y = a.y.min(b.y.min(c.y)).max(0);
78        let max_y = a.y.max(b.y.max(c.y)).min(SCREEN_Y as isize - 1);
79
80        for y in min_y..=max_y {
81            for x in min_x..=max_x {
82                let p = Point2D {x, y};
83                if triangle.point_in_triangle(p) {
84                    self.screen[y as usize][x as usize] = Some(color);
85                }
86            }
87        }
88    }
89
90    fn render_face(&mut self, face: &Face) {
91        let projected_markers: Vec<Point2D> = face.markers
92            .iter()
93            .map(|&p| self.project_point(p))
94            .collect();
95
96        for row in 0..3 {
97            for col in 0..3 {
98                let tris = [
99                    Triangle(projected_markers.get(row * 4 + col).unwrap().clone(), 
100                        projected_markers.get(row * 4 + col + 1).unwrap().clone(), 
101                        projected_markers.get((row + 1) * 4 + col + 1).unwrap().clone()
102                    ),
103                    Triangle(projected_markers.get(row * 4 + col).unwrap().clone(), 
104                        projected_markers.get((row + 1) * 4 + col + 1).unwrap().clone(),
105                        projected_markers.get((row + 1) * 4 + col).unwrap().clone()
106                    ),
107                ];
108                let color = face.grid_face.grid[row][col];
109                for tri in tris {
110                    self.rasterize_triangle(tri, color);
111                }
112            }
113        }
114    }
115
116    fn render_face_slice(&mut self, face_slice: &FaceSlice) {
117        let projected_markers: Vec<Point2D> = face_slice.markers
118            .iter()
119            .map(|&p| self.project_point(p))
120            .collect();
121
122        for row in 0..3 {
123            let tris = [
124                Triangle(projected_markers.get(row * 2).unwrap().clone(), 
125                    projected_markers.get(row * 2 + 1).unwrap().clone(), 
126                    projected_markers.get((row + 1) * 2 + 1).unwrap().clone()
127                ),
128                Triangle(projected_markers.get(row * 2).unwrap().clone(), 
129                    projected_markers.get((row + 1) * 2 + 1).unwrap().clone(),
130                    projected_markers.get((row + 1) * 2).unwrap().clone()
131                ),
132            ];
133            let color = face_slice.colors[row];
134            for tri in tris {
135                self.rasterize_triangle(tri, color);
136            }
137        }
138    }
139
140    pub fn render(&mut self, mut renderables: Vec<&dyn Renderable>) {
141        renderables.sort_by(|a, b| a.dist().partial_cmp(&b.dist()).unwrap());
142
143        for renderable in renderables.into_iter().rev() {
144            let faces = renderable.get_visible_faces();
145            for face in faces.into_iter().take(3).rev() {
146                match face {
147                    AnyFace::Face(f) => self.render_face(&f),
148                    AnyFace::FaceSlice(fs) => self.render_face_slice(&fs),
149                };
150            }
151        }
152    }
153
154    pub fn print_screen(&self) {
155        for y in (0..(SCREEN_Y)).rev() {
156            for x in 0..(SCREEN_X) {
157                match self.screen[y][x] {
158                    Some(color) => print!("{}{}{}", color.to_ansi(), PRINT_CHAR, ANSI_RESET),
159                    _ => print!("  ")
160                };
161            }
162            println!();
163        }
164        io::stdout().flush().unwrap();
165    }
166
167    pub fn reset_terminal() {
168        print!("{esc}c", esc = 27 as char);
169        io::stdout().flush().unwrap();
170    }
171
172    pub fn clear_screen(&mut self) {
173        for row in self.screen.iter_mut() {
174            for cell in row.iter_mut() {
175                *cell = None;
176            }
177        }
178    }
179}