|
2 | 2 | #include <cmath> |
3 | 3 | #include <limits> |
4 | 4 | #include "tgaimage.h" |
| 5 | +#include "model.h" |
5 | 6 | #include "geometry.h" |
6 | 7 |
|
7 | | -const TGAColor white = TGAColor(255, 255, 255, 255); |
8 | | -const TGAColor red = TGAColor(255, 0, 0, 255); |
9 | | -const TGAColor green = TGAColor(0, 255, 0, 255); |
10 | | -const TGAColor blue = TGAColor(0, 0, 255, 255); |
11 | 8 | const int width = 800; |
12 | | -const int height = 500; |
| 9 | +const int height = 800; |
| 10 | +const int depth = 255; |
13 | 11 |
|
14 | | -void line(Vec2i p0, Vec2i p1, TGAImage &image, TGAColor color) { |
15 | | - bool steep = false; |
16 | | - if (std::abs(p0.x-p1.x)<std::abs(p0.y-p1.y)) { |
17 | | - std::swap(p0.x, p0.y); |
18 | | - std::swap(p1.x, p1.y); |
19 | | - steep = true; |
20 | | - } |
21 | | - if (p0.x>p1.x) { |
22 | | - std::swap(p0, p1); |
23 | | - } |
24 | | - |
25 | | - for (int x=p0.x; x<=p1.x; x++) { |
26 | | - float t = (x-p0.x)/(float)(p1.x-p0.x); |
27 | | - int y = p0.y*(1.-t) + p1.y*t + .5; |
28 | | - if (steep) { |
29 | | - image.set(y, x, color); |
30 | | - } else { |
31 | | - image.set(x, y, color); |
32 | | - } |
33 | | - } |
34 | | -} |
| 12 | +Model *model = NULL; |
| 13 | +int *zbuffer = NULL; |
| 14 | +Vec3f light_dir(0,0,-1); |
35 | 15 |
|
36 | | -void rasterize(Vec2i p0, Vec2i p1, TGAImage &image, TGAColor color, int ybuffer[]) { |
37 | | - if (p0.x>p1.x) { |
38 | | - std::swap(p0, p1); |
39 | | - } |
40 | | - for (int x=p0.x; x<=p1.x; x++) { |
41 | | - float t = (x-p0.x)/(float)(p1.x-p0.x); |
42 | | - int y = p0.y*(1.-t) + p1.y*t + .5; |
43 | | - if (ybuffer[x]<y) { |
44 | | - ybuffer[x] = y; |
45 | | - image.set(x, 0, color); |
| 16 | +void triangle(Vec3i t0, Vec3i t1, Vec3i t2, TGAImage &image, TGAColor color, int *zbuffer) { |
| 17 | + if (t0.y==t1.y && t0.y==t2.y) return; // i dont care about degenerate triangles |
| 18 | + if (t0.y>t1.y) std::swap(t0, t1); |
| 19 | + if (t0.y>t2.y) std::swap(t0, t2); |
| 20 | + if (t1.y>t2.y) std::swap(t1, t2); |
| 21 | + int total_height = t2.y-t0.y; |
| 22 | + for (int i=0; i<total_height; i++) { |
| 23 | + bool second_half = i>t1.y-t0.y || t1.y==t0.y; |
| 24 | + int segment_height = second_half ? t2.y-t1.y : t1.y-t0.y; |
| 25 | + float alpha = (float)i/total_height; |
| 26 | + float beta = (float)(i-(second_half ? t1.y-t0.y : 0))/segment_height; // be careful: with above conditions no division by zero here |
| 27 | + Vec3i A = t0 + (t2-t0)*alpha; |
| 28 | + Vec3i B = second_half ? t1 + (t2-t1)*beta : t0 + (t1-t0)*beta; |
| 29 | + if (A.x>B.x) std::swap(A, B); |
| 30 | + for (int j=A.x; j<=B.x; j++) { |
| 31 | + float phi = B.x==A.x ? 1. : (float)(j-A.x)/(float)(B.x-A.x); |
| 32 | + Vec3i P = A + (B-A)*phi; |
| 33 | + P.x = j; P.y = t0.y+i; // a hack to fill holes (due to int cast precision problems) |
| 34 | + int idx = j+(t0.y+i)*width; |
| 35 | + if (zbuffer[idx]<P.z) { |
| 36 | + zbuffer[idx] = P.z; |
| 37 | + image.set(P.x, P.y, color); // attention, due to int casts t0.y+i != A.y |
| 38 | + } |
46 | 39 | } |
47 | 40 | } |
48 | 41 | } |
49 | 42 |
|
50 | 43 | int main(int argc, char** argv) { |
51 | | - { // just dumping the 2d scene (yay we have enough dimensions!) |
52 | | - TGAImage scene(width, height, TGAImage::RGB); |
53 | | - |
54 | | - // scene "2d mesh" |
55 | | - line(Vec2i(20, 34), Vec2i(744, 400), scene, red); |
56 | | - line(Vec2i(120, 434), Vec2i(444, 400), scene, green); |
57 | | - line(Vec2i(330, 463), Vec2i(594, 200), scene, blue); |
58 | | - |
59 | | - // screen line |
60 | | - line(Vec2i(10, 10), Vec2i(790, 10), scene, white); |
| 44 | + if (2==argc) { |
| 45 | + model = new Model(argv[1]); |
| 46 | + } else { |
| 47 | + model = new Model("obj/african_head.obj"); |
| 48 | + } |
61 | 49 |
|
62 | | - scene.flip_vertically(); // i want to have the origin at the left bottom corner of the image |
63 | | - scene.write_tga_file("scene.tga"); |
| 50 | + zbuffer = new int[width*height]; |
| 51 | + for (int i=0; i<width*height; i++) { |
| 52 | + zbuffer[i] = std::numeric_limits<int>::min(); |
64 | 53 | } |
65 | 54 |
|
66 | | - { |
67 | | - TGAImage render(width, 16, TGAImage::RGB); |
68 | | - int ybuffer[width]; |
69 | | - for (int i=0; i<width; i++) { |
70 | | - ybuffer[i] = std::numeric_limits<int>::min(); |
| 55 | + { // draw the model |
| 56 | + TGAImage image(width, height, TGAImage::RGB); |
| 57 | + for (int i=0; i<model->nfaces(); i++) { |
| 58 | + std::vector<int> face = model->face(i); |
| 59 | + Vec3i screen_coords[3]; |
| 60 | + Vec3f world_coords[3]; |
| 61 | + for (int j=0; j<3; j++) { |
| 62 | + Vec3f v = model->vert(face[j]); |
| 63 | + screen_coords[j] = Vec3i((v.x+1.)*width/2., (v.y+1.)*height/2., (v.z+1.)*depth/2.); |
| 64 | + world_coords[j] = v; |
| 65 | + } |
| 66 | + Vec3f n = (world_coords[2]-world_coords[0])^(world_coords[1]-world_coords[0]); |
| 67 | + n.normalize(); |
| 68 | + float intensity = n*light_dir; |
| 69 | + if (intensity>0) { |
| 70 | + triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, TGAColor(intensity*255, intensity*255, intensity*255, 255), zbuffer); |
| 71 | + } |
71 | 72 | } |
72 | | - rasterize(Vec2i(20, 34), Vec2i(744, 400), render, red, ybuffer); |
73 | | - rasterize(Vec2i(120, 434), Vec2i(444, 400), render, green, ybuffer); |
74 | | - rasterize(Vec2i(330, 463), Vec2i(594, 200), render, blue, ybuffer); |
75 | 73 |
|
76 | | - // 1-pixel wide image is bad for eyes, lets widen it |
| 74 | + image.flip_vertically(); // i want to have the origin at the left bottom corner of the image |
| 75 | + image.write_tga_file("output.tga"); |
| 76 | + } |
| 77 | + |
| 78 | + { // dump z-buffer (debugging purposes only) |
| 79 | + TGAImage zbimage(width, height, TGAImage::GRAYSCALE); |
77 | 80 | for (int i=0; i<width; i++) { |
78 | | - for (int j=1; j<16; j++) { |
79 | | - render.set(i, j, render.get(i, 0)); |
| 81 | + for (int j=0; j<height; j++) { |
| 82 | + zbimage.set(i, j, TGAColor(zbuffer[i+j*width], 1)); |
80 | 83 | } |
81 | 84 | } |
82 | | - render.flip_vertically(); // i want to have the origin at the left bottom corner of the image |
83 | | - render.write_tga_file("render.tga"); |
| 85 | + zbimage.flip_vertically(); // i want to have the origin at the left bottom corner of the image |
| 86 | + zbimage.write_tga_file("zbuffer.tga"); |
84 | 87 | } |
| 88 | + delete model; |
| 89 | + delete [] zbuffer; |
85 | 90 | return 0; |
86 | 91 | } |
87 | 92 |
|
0 commit comments