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 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 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 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 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}