#include #include #include #include #include // some types for later use typedef unsigned char byte; typedef byte rgb[3]; // these are 3 byte pixels in RGB order // a few colors to use static rgb red = { 255, 0, 0 }; static rgb blue = { 0, 0, 255 }; static rgb black = {0,0,0}; int num_vert; struct vertex { rgb color; double x, y, z; void fill_vertex(double gx, double gy, double gz, const rgb p) { x = gx; y = gy; z = gz; color[0] = p[0]; color[1] = p[1]; color[2] = p[2]; } } *list; // an object to represent the framebuffer where I draw struct FrameBuffer { int width, height; rgb *pix; double *pz; //pixel z value. FrameBuffer(int w, int h) { width=w; height=h; pix = new rgb[width*height]; pz = new double[width*height];} void Clear(const rgb p); void TriDraw(vertex list[], int si, double times[], double nlines[], double npixels[], int ind); void Test(vertex list[], int index); } *fb; // global framebuffer object for use in callbacks static vinc = sizeof(vertex); /* struct TriList { vertex *list; TriList(int num) {list = (vertex *) malloc(num*vinc);} //TriList(){} } *tl; //global vertext list, interpreted as triangle list. */ void FrameBuffer::Clear(const rgb p) { register byte r = p[0]; register byte g = p[1]; register byte b = p[2]; register byte* p0 = pix[0]; register byte* p1 = p0 + sizeof(rgb)*width*height; while(p0 < p1) { p0[0] = r; p0[1] = g; p0[2] = b; p0 += 3; } } void FrameBuffer::Test(vertex list[], int index) { int i, j; i = (height - 1)*list[index].y; j = (width - 1)*list[index].x; register byte* p = pix[i*width + j]; //(0,0) p[0] = (byte) 255; p[1] = (byte) 0; p[2] = (byte) 0; i = (height - 1)*list[index + 1].y; j = (width - 1)*list[index + 1].x; p = pix[i*width + j]; //(1,0) p[0] = (byte) 0; p[1] = (byte) 255; p[2] = (byte) 0; i = (height - 1)*list[index + 2].y; j = (width - 1)*list[index + 2].x; p = pix[i*width + j]; //(1,1) p[0] = (byte) 0; p[1] = (byte) 0; p[2] = (byte) 255; } void FrameBuffer::TriDraw(vertex list[], int si, double times[], double nlines[], double npixels[], int ind) /*Draws the visible part of a triangle specified by the array of three verticies. First, I mention that even with floating point error, it will be easier to do the calculations at integer coordinates rather than 1/width and 1/height increments from zero to one. So then, the idea here is to look at the bounding box of the triangle and render that whole region in the same way: if the integer (pixel) coord is inside the triangle and its associated z is large enough, render the associated color. */ { clock_t stime, ftime; stime = clock(); int i, j, npix = 0; double xv[3], yv[3], zv[3], x[3],y[3], z[3], k[3], be[3]; //These are vector components that follow the //triangle in the same direction. double bx1, bx2, by1, by2; //The bounding box on the triangle, both the values //and the vertex number that lead to it. The bu, bd, //etc. are the order of the verticies, which is //constant for a row. double eval1, eval2, eval3; //The evaluated line equations determining what side of each //triangle line a point is on. //double sb = 0x8000000000000000; double a, b, c, iz[2], ix[2], t[3], dx, cz; int l1, l2; //which sides to use in interpolation. rgb co[2]; xv[0] = (width - 1)*(list[si + 1].x - list[si + 0].x); xv[1] = (width - 1)*(list[si + 2].x - list[si + 1].x); xv[2] = (width - 1)*(list[si + 0].x - list[si + 2].x); yv[0] = (height-1)*(list[si + 1].y - list[si + 0].y); yv[1] = (height-1)*(list[si + 2].y - list[si + 1].y); yv[2] = (height-1)*(list[si + 0].y - list[si + 2].y); zv[0] = list[si + 1].z - list[si + 0].z; zv[1] = list[si + 2].z - list[si + 1].z; zv[2] = list[si + 0].z - list[si + 2].z; x[0] = (width-1)*list[si + 0].x; x[1] = (width-1)*list[si + 1].x; x[2] = (width-1)*list[si + 2].x; y[0] = (height-1)*list[si + 0].y; y[1] = (height-1)*list[si + 1].y; y[2] = (height-1)*list[si + 2].y; z[0] = list[si + 0].z; z[1] = list[si + 1].z; z[2] = list[si + 2].z; //This below determines a bounding box for the triangle. if (x[0] <= x[1]) { if (x[1] <= x[2]) { bx1 = floor(x[0]); bx2 = ceil(x[2]); }else if (x[2] <= x[0]) { bx1 = floor(x[2]); bx2 = ceil(x[1]); }else { bx1 = floor(x[0]); bx2 = ceil(x[1]); } }else { if (x[0] <= x[2]) { bx1 = floor(x[1]); bx2 = ceil(x[2]); }else if (x[1] <= x[2]) { bx1 = floor(x[1]); bx2 = ceil(x[0]); }else { bx1 = floor(x[2]); bx2 = ceil(x[0]); } } if (y[0] <= y[1]) { if (y[1] <= y[2]) { by1 = floor(y[0]); by2 = ceil(y[2]); }else if (y[2] <= y[0]) { by1 = floor(y[2]); by2 = ceil(y[1]); }else { by1 = floor(y[0]); by2 = ceil(y[1]); } }else { if (y[0] <= y[2]) { by1 = floor(y[1]); by2 = ceil(y[2]); }else if (y[1] <= y[2]) { by1 = floor(y[1]); by2 = ceil(y[0]); }else { by1 = floor(y[2]); by2 = ceil(y[0]); } } //Now get the equations of the lines. for example, //the side going from vertex 1 to 2 is //y - kx + b where k = yv[0]/xv[0] and b = kx[1] - y[1]. if (fabs(xv[0]) <= 0.0001) { k[0] = 1.0; be[0] = 1.0*x[1]; }else { k[0] = yv[0]/xv[0]; be[0] = k[0]*x[1] - y[1]; } if (fabs(xv[1]) <= 0.0001) { k[1] = 1.0; be[1] = 1.0*x[2]; }else { k[1] = yv[1]/xv[1]; be[1] = k[1]*x[2] - y[2]; } if (fabs(xv[2]) <= 0.0001) { k[2] = 1.0; be[2] = 1.0*x[0]; }else { k[2] = yv[2]/xv[2]; be[2] = k[2]*x[0] - y[0]; } nlines[ind] = by2 - by1; //Okay, now we have a bounding box for the triangle. //Now, for every row, for every column in the bounding //box, decide if the pixel should be drawn and perhaps //draw it. //cerr << "by1 " << (int) by1 << " by2" << (int) by2 << endl; for (i = (int) by1; i <= (int) by2; i ++) { j = (int) bx1; if (xv[0] <= 0.0) { if (fabs(xv[0]) <= 0.00001) { if (yv[0] >= 0.0) eval1 = -1.0*k[0]*j + be[0]; else eval1 = k[0]*j - be[0]; }else eval1 = -i + k[0]*j - be[0]; //k[0] = -1.0*k[0]; }else { eval1 = i - k[0]*j + be[0]; } if (xv[1] <= 0.0) { if (fabs(xv[1]) <= 0.00001) { if (yv[1] >= 0.0) eval2 = -1.0*k[1]*j + be[1]; else eval2 = k[1]*j - be[1]; }else eval2 = -i + k[1]*j - be[1]; //k[1] = -1.0*k[1]; }else { eval2 = i - k[1]*j + be[1]; } if (xv[2] <= 0.0) { if (fabs(xv[2]) <= 0.00001) { if (yv[2] >= 0.0) eval3 = -1.0*k[2]*j + be[2]; else eval3 = k[2]*j - be[2]; }else eval3 = -i + k[2]*j - be[2]; //k[2] = -1.0*k[2]; }else { eval3 = i - k[2]*j + be[2]; } //We can //determine which two lines to interpolate along first. //These two will stay the same for the entire row. if (((floor(y[0]) < i)&&(i < ceil(y[1])))||((ceil(y[0]) > i)&&(i > floor(y[1])))) { l1 = 0; if (((floor(y[1]) < i)&&(i < ceil(y[2])))||((ceil(y[1]) > i)&&(i > floor(y[2])))) l2 = 1; else l2 = 2; }else { l1 = 1; l2 = 2; } //Now we interpolate z with respect to y, and set beginning and //ending x's for the inner loop. t[0] = fabs((fabs(i - y[l1]))/yv[l1]); //The t of inTerpolation. if(t[0] > 1.0) t[0] = 1.0; iz[0] = (1.0 - t[0])*z[l1] + t[0]*z[(l1 + 1)%3]; ix[0] = (1.0 - t[0])*x[l1] + t[0]*x[(l1 + 1)%3]; t[1] = fabs((fabs(i - y[l2]))/yv[l2]); if (t[1] > 1.0) t[1] = 1.0; iz[1] = (1.0 - t[1])*z[l2] + t[1]*z[(l2 + 1)%3]; ix[1] = (1.0 - t[1])*x[l2] + t[1]*x[(l2 + 1)%3]; dx = fabs(ix[0] - ix[1]); for (j; j <= (int) bx2; j ++) { if (xv[0] <= 0.0) { if (fabs(xv[0]) <= 0.00001) { if (yv[0] >= 0.0) eval1 -= k[0]; else eval1 += k[0]; }else eval1 += k[0]; //k[0] = -1.0*k[0]; }else { eval1 -= k[0]; } if (xv[1] <= 0.0) { if (fabs(xv[1]) <= 0.00001) { if (yv[1] >= 0.0) eval2 -= k[1]; else eval2 += k[1]; }else eval2 += k[1]; //k[1] = -1.0*k[1]; }else { eval2 -= k[1]; } if (xv[2] <= 0.0) { if (fabs(xv[2]) <= 0.00001) { if (yv[2] >= 0.0) eval3 -= k[2]; else eval3 += k[2]; }else eval3 += k[2]; //k[2] = -1.0*k[2]; }else { eval3 -= k[2]; } a = eval1; b = eval2; c = eval3; //Big, ugly test to check against zeros. if (eval1 == 0.0) { if (eval2 != 0.0) { a = b; if (eval3 == 0.0) c = b; } else if (eval3 != 0.0) { a = c; b = c; } } else if (eval2 == 0.0) { b = a; if (eval3 == 0.0) c = a; } else if (eval3 == 0.0) { c = a; } //Now finally the check for whether this pixel //will be colored. //cerr << "About to decide: a " << a << " b " << b << " c " << c << endl; if (((a < 0.0) && (b < 0.0) && (c < 0.0))||((a > 0.0) && (b > 0.0) && (c > 0.0))) { //The pixel is in the triangle. //We need to linearly interpolate z and then //perhaps the colors too. This is the funcitonal part of the whole thing. t[2] = fabs(j - ix[0])/dx; if (t[2] > 1.0) t[2] = 1.0; cz = (1.0-t[2])*iz[0] + t[2]*iz[1]; //The current pixel's calculated z. if (pz[i*width + j] <= cz) { //Then we color the pixel. pz[i*width + j] = cz; co[0][0] = (byte) ((1.0 - t[0])*list[si+l1].color[0] + t[0]*list[si+((l1+1)%3)].color[0]); co[0][1] = (byte) ((1.0 - t[0])*list[si+l1].color[1] + t[0]*list[si+((l1+1)%3)].color[1]); co[0][2] = (byte) ((1.0 - t[0])*list[si+l1].color[2] + t[0]*list[si+((l1+1)%3)].color[2]); co[1][0] = (byte) ((1.0 - t[1])*list[si+l2].color[0] + t[1]*list[si+((l2+1)%3)].color[0]); co[1][1] = (byte) ((1.0 - t[1])*list[si+l2].color[1] + t[1]*list[si+((l2+1)%3)].color[1]); co[1][2] = (byte) ((1.0 - t[1])*list[si+l2].color[2] + t[1]*list[si+((l2+1)%3)].color[2]); register byte *p = pix[i*width + j]; p[0] = (byte) ((1.0-t[2])*co[0][0] + t[2]*co[1][0]); p[1] = (byte) ((1.0-t[2])*co[0][1] + t[2]*co[1][1]); p[2] = (byte) ((1.0-t[2])*co[0][2] + t[2]*co[1][2]); // if (((int) co[0][0] == (int) p[0])&&((int) co[0][1] == (int) p[1])&&((int) co[0][2] == (int) p[2])) // cerr << t[0] << " " << t[1] << " " << t[2] << " " << i << " " << j << endl // << " " << x[0] << " " << y[0] << ", " << x[1] << " " << y[1] << ", " // << x[2] << " " << y[2] << ", l1 = " << l1 << ", l2 = " << l2 << endl // << "\n " << (int) p[0] << " " << (int) p[1] << " " << (int) p[2] << endl; // if (abs(230 - (int) p[2]) <= 5) { // cerr << "I don't get it.\n"; // } // glDrawPixels(fb->width, fb->height, GL_RGB, GL_UNSIGNED_BYTE, fb->pix); // glFlush(); npix ++; } } } } ftime = clock(); times[ind] = 1.0*(ftime - stime)/CLOCKS_PER_SEC; npixels[ind] = npix; } void Get_RandTri() { //This function will put a random triangle in list. //cerr << RAND_MAX << endl; for (int i = 0; i < 3; i ++) { list[i].x = 1.0*rand()/RAND_MAX; list[i].y = 1.0*rand()/RAND_MAX; list[i].z = 1.0*rand()/RAND_MAX; //cerr << list[i].x << " " << list[i].y << " " << list[i].z ; list[i].color[0] = (byte) 255*(1.0*rand())/RAND_MAX; list[i].color[1] = (byte) 255*(1.0*rand())/RAND_MAX; list[i].color[2] = (byte) 255*(1.0*rand())/RAND_MAX; //cerr << " " << (int) list[i].color[0] << " " << (int) list[i].color[1] << " " << (int) list[i].color[2] << endl; } } void Get_Triangles(ifstream &fin) //Fills the triangle info array. { double x, y, z; int num, i, temp[3]; rgb color; fin >> num; num = 3*num; num_vert = num; //tl = new TriList(num); list = new vertex[num]; for (i = 1; i <= num; i++) { //fin.getline(line, sizeof(line)); //get_xyzrgb(line, x, y, z, color); fin >> x >> y >> z; // cerr << x << " " << y << " " << z << " is x y z. "; fin >> temp[0] >> temp[1] >> temp[2]; color[0] = (byte) temp[0]; color[1] = (byte) temp[1]; color[2] = (byte) temp[2]; // cerr << (int) color[0] << " " << (int) color[1] << " " // << (int) color[2] << " are the r g b.\n"; list[(i-1)].fill_vertex(x,y,z,color); } } void myDisplay(void) { // copy the framebuffer to the screen glDrawPixels(fb->width, fb->height, GL_RGB, GL_UNSIGNED_BYTE, fb->pix); glFlush(); } void myDraw() { // I do my drawing into the framebuffer object here // this is just a hack example fb->Clear(black); // then transfer to the screen // I can do this as often as I like, even after every pixel for debugging purposes. //for (int i = 0; i < num_vert; i += 3) // fb->TriDraw(list, i); //fb->Test(list, 0); //The code has gotten messy as I time the experiments. double times[500], nlines[500], npixels[500]; int i; srand( time(NULL) ); // for(i = 1; i <= 100; i ++) { for (i = 0; i < 50; i ++) { Get_RandTri(); fb->TriDraw(list, 0, times, nlines, npixels, i); } //for (i = 0; i < 50; i++) // cerr << "time " << times[i] << ", nlines " << nlines[i] << ", npixels " << npixels[i] << endl; ofstream fout; fout.open("output.txt"); for (i = 0; i < 50; i ++) { fout << times[i] << " " << nlines[i] << " " << npixels[i] << endl; } fout.close(); myDisplay(); } void myInit() { // initialize GL stuff for speed #if 1 // this makes quite a difference on the PC with software GL glDisable(GL_DITHER); #endif // SGI claims some of these might produce speedups for some architectures #if 0 glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDisable(GL_FOG); glDisable(GL_LIGHTING); glDisable(GL_LOGIC_OP); glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_1D); glDisable(GL_TEXTURE_2D); glPixelTransferi(GL_MAP_COLOR, GL_FALSE); glPixelTransferi(GL_RED_SCALE, 1); glPixelTransferi(GL_RED_BIAS, 0); glPixelTransferi(GL_GREEN_SCALE, 1); glPixelTransferi(GL_GREEN_BIAS, 0); glPixelTransferi(GL_BLUE_SCALE, 1); glPixelTransferi(GL_BLUE_BIAS, 0); glPixelTransferi(GL_ALPHA_SCALE, 1); glPixelTransferi(GL_ALPHA_BIAS, 0); #endif } void KeyInputHandler(unsigned char Key, int x, int y) { switch(Key) { case 27: exit(0); default: myDraw(); break; }; } void main(int argc, char* argv[]) { int width=512, height=512; ifstream fin; char *filename = new char[80]; fb = new FrameBuffer(width, height); glutInitDisplayMode(GLUT_RGB); glutInitWindowPosition(50, 50); glutInitWindowSize(width, height); glutCreateWindow("COMP 236 - ASSN2"); glutKeyboardFunc(KeyInputHandler); glutDisplayFunc(myDisplay); myInit(); cerr << CLOCKS_PER_SEC << endl; //cerr << "Please enter the triangle file name:\n"; //cin >> filename; /*fin.open(filename); if (fin == NULL) { cerr << "Input File Stream Null.\n"; exit(0); } Get_Triangles(fin); */ list = new vertex[3]; glutMainLoop(); }