rusterize/
renderer.rs

1use std::cmp::Ordering::Equal;
2use std::error;
3use std::f64;
4use std::mem;
5
6use pixel;
7use pixel::Pixel;
8use screen::Screen;
9use texture::Texture;
10use types::*;
11
12
13macro_rules! do_with_color {
14    ( $renderer:expr, $color:expr, $block:block ) => {
15        let old_color = $renderer.color;
16        $renderer.color = $color;
17        $block;
18        $renderer.color = old_color;
19    }
20}
21
22
23pub enum LightingMode {
24    NoShading,
25    FlatShading,
26}
27
28pub struct Renderer<S>
29    where S: Screen
30{
31    screen: S,
32    texture: Texture,
33
34    transform: Transform,
35    color: Pixel,
36
37    light: Point,
38    lighting_mode: LightingMode,
39}
40
41#[allow(dead_code)]
42impl<S> Renderer<S>
43    where S: Screen
44{
45    pub fn new(screen: S) -> Renderer<S> {
46        let w = screen.width();
47        let h = screen.height();
48
49        Renderer {
50            screen: screen,
51            texture: Texture::new(w, h),
52
53            transform: Transform::identity(),
54            color: pixel::WHITE,
55
56            light: pt![0., 0., 0.],
57            lighting_mode: LightingMode::NoShading,
58        }
59    }
60
61    pub fn draw_point(&mut self, p: Point) {
62        let p = p * self.transform;
63        let d = 7;
64        for row in 0 .. d {
65            self.texture.set_row(
66                p.x as PixCoord - d / 2,
67                p.x as PixCoord + d / 2,
68                p.y as PixCoord + row - d / 2,
69                f64::NEG_INFINITY,
70                f64::NEG_INFINITY,
71                self.color
72            );
73        }
74    }
75
76    fn draw_point_with_transform(&mut self, p: Point, transform: Transform) {
77        let old_transform = self.transform;
78        self.transform = transform;
79        self.draw_point(p);
80        self.transform = old_transform;
81    }
82
83    pub fn draw_line(&mut self, p1: Point, p2: Point) {
84        let p1 = p1 * self.transform;
85        let p2 = p2 * self.transform;
86        let p1x = p1.x as PixCoord;
87        let p1y = p1.y as PixCoord;
88        let p2x = p2.x as PixCoord;
89        let p2y = p2.y as PixCoord;
90
91        let dx = p2.x as i64 - p1.x as i64;
92        let dy = p2.y as i64 - p1.y as i64;
93        let adx = if dx >= 0 { dx } else { -dx };
94        let ady = if dy >= 0 { dy } else { -dy };
95
96        let x_step = if p2x > p1x { 1 } else { -1 };
97        let y_step = if p2y > p1y { 1 } else { -1 };
98        let mut x = p1x;
99        let mut y = p1y;
100        let mut error: i64 = 0;
101        loop {
102            if adx >= ady {
103                if 2 * error > adx {
104                    y += y_step;
105                    error -= adx;
106                }
107                error += ady;
108            } else {
109                if 2 * error > ady {
110                    x += x_step;
111                    error -= ady;
112                }
113                error += adx;
114            }
115
116            // FIXME: Do depth lerping.
117            self.texture.set_pixel(
118                x,
119                y,
120                f64::INFINITY,
121                self.color
122            );
123
124            if adx >= ady {
125                if x == p2x { break }
126                else { x += x_step }
127            } else {
128                if y == p2y { break }
129                else { y += y_step }
130            }
131        }
132    }
133
134    fn draw_line_with_transform(
135        &mut self,
136        p1: Point,
137        p2: Point,
138        transform: Transform
139    ) {
140        let old_transform = self.transform;
141        self.transform = transform;
142        self.draw_line(p1, p2);
143        self.transform = old_transform;
144    }
145
146    pub fn draw_triangle(&mut self, t: Triangle) {
147        self.draw_line(t.p1, t.p2);
148        self.draw_line(t.p2, t.p3);
149        self.draw_line(t.p3, t.p1);
150    }
151
152    pub fn fill_triangle(&mut self, t: Triangle) {
153        // Backface culling.
154        let centroid = (t.p1 + t.p2 + t.p3) * (1. / 3.);
155        let ct = t * self.transform;
156        if ct.normal().dot(centroid) >= 0. { return }
157
158        // Sort points by y coord.
159        let mut pts = ct.to_arr();
160        pts.sort_by(
161            |p1, p2|
162            p1.y.partial_cmp(&p2.y)
163                .unwrap_or(Equal)
164        );
165        let (top, middle, bot) = (pts[0], pts[1], pts[2]);
166
167        // Compute color of triangle based on light.
168        let old_color = self.color;
169        self.color = self.light_triangle(t, old_color);
170
171        const EPSILON: f64 = 1.;
172        if middle.y - top.y < EPSILON {
173            self.fill_top_flat_triangle(Triangle::from_arr(pts));
174        } else if bot.y - middle.y < EPSILON {
175            self.fill_bottom_flat_triangle(Triangle::from_arr(pts));
176        } else {
177            let dy_mid: Coord = middle.y - top.y;
178            let dy_bot: Coord = bot.y - top.y;
179            let dx_bot: Coord = bot.x - top.x;
180            let dz_bot: Coord = bot.z - top.z;
181
182            let v4 = pt![
183                top.x + dx_bot * dy_mid / dy_bot,
184                middle.y,
185                top.z + dz_bot * dy_mid / dy_bot
186            ];
187            self.fill_bottom_flat_triangle(trigon![top, middle, v4]);
188            self.fill_top_flat_triangle(trigon![middle, v4, bot]);
189        }
190
191        self.color = old_color;
192    }
193
194    fn light_triangle(&self, t: Triangle, color: Pixel) -> Pixel {
195        match self.lighting_mode {
196            LightingMode::NoShading => color,
197            LightingMode::FlatShading => {
198                let centroid = (t.p1 + t.p2 + t.p3) * (1. / 3.);
199                let light_dir = (self.light - centroid).normalized();
200                let light_mag = light_dir.dot(t.normal()).max(0.);
201                let (r, g, b) = color;
202                (
203                    (r as f64 * light_mag) as u8,
204                    (g as f64 * light_mag) as u8,
205                    (b as f64 * light_mag) as u8
206                )
207            },
208        }
209    }
210
211    fn fill_bottom_flat_triangle(&mut self, t: Triangle) {
212        let (top, mut left, mut right) = t.to_tuple();
213        if left.x > right.x { mem::swap(&mut left, &mut right) }
214
215        for y in top.y as PixCoord .. left.y as PixCoord {
216            let t = (y - top.y as PixCoord) as Coord / (left.y - top.y);
217
218            let z_left  = top.z + t * (left.z  - top.z);
219            let z_right = top.z + t * (right.z - top.z);
220
221            self.texture.set_row(
222                (top.x + (left.x  - top.x) * t) as PixCoord,
223                (top.x + (right.x - top.x) * t) as PixCoord,
224                y,
225                z_left,
226                z_right,
227                self.color
228            );
229        }
230    }
231
232    fn fill_top_flat_triangle(&mut self, t: Triangle) {
233        let (mut left, mut right, bot) = t.to_tuple();
234        if left.x > right.x { mem::swap(&mut left, &mut right) }
235
236        for y in left.y as PixCoord .. bot.y as PixCoord + 1 {
237            let t       = (y - left.y as PixCoord) as Coord / (bot.y - left.y);
238            let z_left  = left.z  + t * (bot.z - left.z);
239            let z_right = right.z + t * (bot.z - right.z);
240
241            self.texture.set_row(
242                (left.x  + (bot.x - left.x)  * t) as PixCoord,
243                (right.x + (bot.x - right.x) * t) as PixCoord,
244                y,
245                z_left,
246                z_right,
247                self.color
248            );
249        }
250    }
251
252    pub fn clear(&mut self) {
253        self.texture.clear();
254    }
255
256    pub fn display(&mut self) -> Result<(), Box<error::Error>> {
257        self.screen.display_texture(&self.texture)
258    }
259
260
261    pub fn set_transform(&mut self, t: Transform) {
262        self.transform = t;
263    }
264
265    pub fn clear_transform(&mut self) {
266        self.transform = Transform::identity();
267    }
268
269    pub fn translate(&mut self, p: Point) {
270        self.transform = Transform::translate(p) * self.transform;
271    }
272
273    pub fn rotate_x(&mut self, theta: f64) {
274        self.transform = Transform::rotate_x(theta) * self.transform;
275    }
276
277    pub fn rotate_y(&mut self, theta: f64) {
278        self.transform = Transform::rotate_y(theta) * self.transform;
279    }
280
281    pub fn rotate_z(&mut self, theta: f64) {
282        self.transform = Transform::rotate_z(theta) * self.transform;
283    }
284
285    pub fn scale(&mut self, x: f64, y: f64, z: f64) {
286        self.transform = Transform::scale(x, y, z) * self.transform;
287    }
288
289    pub fn perspective(&mut self) {
290        self.transform = Transform::perspective() * self.transform;
291    }
292
293
294    pub fn set_color(&mut self, color: Pixel) { self.color = color; }
295    pub fn set_light_pos(&mut self, pos: Point) { self.light = pos; }
296    pub fn set_lighting_mode(&mut self, lighting_mode: LightingMode) {
297        self.lighting_mode = lighting_mode;
298    }
299}