00001 #include "Canvas.h"
00002 #include "color.h"
00003 #include "xmath.h"
00004 #include <iostream>
00005 #include <fstream>
00006 #include <vector>
00007
00008
00009 using namespace std;
00010
00011 namespace columbia
00012 {
00013
00014 void Canvas :: init()
00015 {
00016 int N = hReso*vReso;
00017
00018 delete [] data;
00019 data = new RGB [N];
00020 memset(data, 0, sizeof(RGB) * N);
00021
00022 delete [] z_buffer;
00023 z_buffer = new float [N];
00024 fill(z_buffer, z_buffer+N, -1.0E9);
00025
00026 eye_at = hReso / 2;
00027 }
00028
00029 void Canvas :: Clear(RGB const& color)
00030 {
00031 for (int row = 0; row < vReso; row++)
00032 for (int col = 0; col < hReso; col++)
00033 {
00034 (*this)[row][col] = color;
00035 }
00036 }
00037
00038 void Canvas :: Checkerboard(int sz, RGB col1, RGB col2)
00039 {
00040 for(int row = 0; row < vReso; row++)
00041 for(int col = 0; col < hReso; col++)
00042 {
00043 (*this)[row][col] = (col/sz + row/sz) % 2 ? col1 : col2;
00044 }
00045 }
00046
00047
00048
00049 void Canvas :: WritePPM(const char *_filename) const
00050 {
00051 Canvas* This = (Canvas*)this;
00052
00053 if(!_filename)
00054 _filename = filename.c_str();
00055
00056 ofstream file;
00057 file.open (_filename, ios::out | ios::binary);
00058
00059 if(file)
00060 {
00061 file << "P6\n";
00062 file << hReso << " " << vReso << endl << 100 << endl;
00063
00064 unsigned char r, g, b;
00065 for (int row = vReso-1; row >= 0; row--)
00066 for (int col = 0; col < hReso; col++)
00067 {
00068 r = (unsigned char) (100.0*(*This)[row][col][0]);
00069 g = (unsigned char) (100.0*(*This)[row][col][1]);
00070 b = (unsigned char) (100.0*(*This)[row][col][2]);
00071
00072 if(row==vReso-1 && col==0)
00073 r = (unsigned char)77, g = (unsigned char)78, b = (unsigned char)79;
00074
00075 if(r>100.0 || g>100.0 || b>100.0) throw "RGB color overflow\n";
00076
00077 file << r << g << b;
00078 }
00079
00080 file << endl;
00081 file.close();
00082 }
00083 else
00084 cerr << "Can't open output file " << _filename << endl;
00085 }
00086
00087
00088 void Canvas :: ReadPPM(const char * _filename)
00089 {
00090 if(!_filename)
00091 _filename = filename.c_str();
00092
00093 ifstream file;
00094 file.open (_filename, ios::in | ios::binary);
00095
00096 if(file)
00097 {
00098 string id;
00099 file >> id;
00100 if(id != "P6") throw "not P6 ppm format\n";
00101
00102 float scale;
00103 file >> hReso >> vReso >> scale;
00104 init();
00105 unsigned char r, g, b;
00106 file >> noskipws >> r;
00107
00108 for (int row = vReso-1; row >= 0; row--)
00109 {
00110 for (int col = 0; col < hReso; col++)
00111 {
00112 file >> noskipws >> r >> noskipws >> g >> noskipws >> b;
00113 if(row==vReso-1 && col==0)
00114 cout << int(r) << "___ " << int(g) << "___ " << int(b) << endl;
00115
00116 (*this)[row][col][0] = r / scale;
00117 (*this)[row][col][1] = g / scale;
00118 (*this)[row][col][2] = b / scale;
00119 }
00120 }
00121
00122 file.close();
00123 }
00124 else
00125 cerr << "Can't open input file " << _filename << endl;
00126 }
00127
00128
00129
00130 void Canvas :: _scanLineSegment(int x1, int y1, float z1, Point const* tex1, RGB const* col1,
00131 int x2, int y2, float z2, Point const* tex2, RGB const* col2,
00132 set<ScannedResult>* output)
00133 {
00134 assert( x2-x1 >= 1 && x2-x1 >= y2-y1 && y2-y1 >= 0);
00135
00136 bool
00137 has_tex = tex1 && tex2,
00138 has_col = col1 && col2,
00139 horizontal = y1==y2,
00140 diagonal = y2-y1==x2-x1;
00141
00142 float scale = 1.0 / (x2 - x1);
00143
00144 Point tex, tex_step;
00145 if(has_tex)
00146 {
00147 tex = *tex1;
00148 tex_step = (*tex2 - *tex1) / (x2-x1);
00149 }
00150
00151 RGB col, col_step;
00152 if(has_col)
00153 {
00154 col = *col1;
00155 col_step = (*col2 - *col1) * scale;
00156 }
00157
00158 int y = y1;
00159 float yy = y1, y_step = (y2 - y1) * scale;
00160 float z = z1, z_step = (z2 - z1) * scale;
00161
00162 for(int x = x1; x <= x2; ++x, z += z_step)
00163 {
00164 writePixel(x, y, z, has_tex? &tex : 0 , has_col? &col : 0, output);
00165
00166 if(has_tex) tex += tex_step;
00167 if(has_col) col += col_step;
00168
00169 if( ! horizontal )
00170 if( diagonal ) y++;
00171 else y = round( yy += y_step );
00172 }
00173 }
00174
00175 void Canvas :: scanLineSegment(int x1, int y1, float z1, Point const* tex1, RGB const* col1,
00176 int x2, int y2, float z2, Point const* tex2, RGB const* col2,
00177 set<ScannedResult>* output)
00178 {
00179 if(x1==x2 && y1==y2) return;
00180
00181 static RGB C1, C2;
00182 RGB *c1 = 0, *c2 = 0;
00183 if(col1 && col2)
00184 {
00185 C1 = *col1, C2 = *col2;
00186 c1 = &C1, c2 = &C2;
00187 }
00188
00189 static Point T1, T2;
00190 Point *t1 = 0, *t2 = 0;
00191 if(tex1 && tex2)
00192 {
00193 T1 = *tex1, T2 = *tex2;
00194 t1 = &T1, t2 = &T2;
00195 }
00196
00197 if(x1 > x2)
00198 {
00199 swap(x1, x2); swap(y1, y2); swap(z1, z2);
00200 if(c1 && c2) swap(*c1, *c2);
00201 if(t1 && t2) swap(*t1, *t2);
00202 }
00203 if( (flip_y = y1 > y2) )
00204 {
00205 y1 = -y1, y2 = -y2;
00206 }
00207 if( (swap_xy = y2-y1 > x2-x1) )
00208 {
00209 swap(x1, y1), swap(x2, y2);
00210 }
00211 _scanLineSegment(x1, y1, z1, t1, c1, x2, y2, z2, t2, c2, output);
00212 }
00213
00214
00215 void Canvas :: SolidTriangle(Point const& P1, Point const* T1, RGB const* col1,
00216 Point const& P2, Point const* T2, RGB const* col2,
00217 Point const& P3, Point const* T3, RGB const* col3)
00218
00219 {
00220 Point p1 = perspectiveProjection(P1);
00221 Point p2 = perspectiveProjection(P2);
00222 Point p3 = perspectiveProjection(P3);
00223
00224 set<ScannedResult> scanned_pnts;
00225 scanLineSegment(round(p1.x), round(p1.y), p1.z, T1, col1, round(p2.x), round(p2.y), p2.z, T2, col2, &scanned_pnts);
00226 scanLineSegment(round(p1.x), round(p1.y), p1.z, T1, col1, round(p3.x), round(p3.y), p3.z, T3, col3, &scanned_pnts);
00227 scanLineSegment(round(p3.x), round(p3.y), p3.z, T3, col3, round(p2.x), round(p2.y), p2.z, T2, col2, &scanned_pnts);
00228
00229 int cur_yval = vReso / 2 + 1;
00230 set<ScannedResult> same_yval;
00231
00232 bool
00233 has_tex = T1 && T2 && T3,
00234 has_col = col1 && col2 && col3;
00235
00236
00237 for (set<ScannedResult>::iterator it = scanned_pnts.begin(); it != scanned_pnts.end(); ++it)
00238 {
00239 int y = it->y;
00240 if(y != cur_yval)
00241 {
00242 if(same_yval.size())
00243 {
00244 set<ScannedResult>::iterator it1 = same_yval.begin(), it2 = --same_yval.end();
00245 scanLineSegment(it1->x, cur_yval, it1->z, has_tex? &it1->tex : 0, has_col? &it1->col : 0,
00246 it2->x, cur_yval, it2->z, has_tex? &it2->tex : 0, has_col? &it2->col : 0);
00247
00248 same_yval.clear();
00249 }
00250 cur_yval = y;
00251 }
00252 same_yval.insert(*it);
00253 }
00254 }
00255 }
00256