// // renderer.cc // // New and improved renderer // // Copyright (C) J. Belson 1999.11.22 // // $Author: jon $ : $Date: 2003/10/11 18:11:00 $ // #include #include #include #include #include "interpolate.h" #include "light.h" #include "settings.h" #include "renderer.h" using namespace std; /** * Constructor */ renderer::renderer(int left, int range, progress* p) : prog(p), fog(false), blend(false) { my_settings = settings::get_instance(); // Figure out which strip we're rendering left_border = left; right_border = left + range; my_settings->get_float(HAZE_INTENSITY, &haze_intensity); my_settings->get_colour(HAZE_COLOUR, &haze); my_settings->get_float(AMBIENT_INTENSITY, &ambient_intensity); my_settings->get_float(DIRECT_INTENSITY, &direct_intensity); // Create light from current settings light.direction = vector3d(0.0, -1.0, 0.0); my_settings->get_colour(DIRECT_COLOUR, &light.col); my_settings->get_colour(AMBIENT_COLOUR, &ambient); } /** * Select whether to use lighting effects */ void renderer::set_lighting(bool b) { lighting = b; } void renderer::set_blend(bool b) { blend = b; }; void renderer::set_fog(bool b) { fog = b; }; /** * Set light source vector */ void renderer::set_light(const vector3d& l) { // For lighting calculations the light vector is the // direction TO the light rather than the ray direction light.direction = -l; } void renderer::set_light_colour(fcolour& col) { light.col = col; } void renderer::set_light_ambient(fcolour& col) { ambient = col; } /** * Pass data to be rendered */ void renderer::setup( const triangle3d* t3d, const point2d* p2d, const vector3d* v3d, int count, unsigned long* fbuf, int bpr, int w, int h, zbuffer* zbuf) { // Store values tri3d = t3d; pnt2d = p2d; vec3d = v3d; num_tri = count; fb = fbuf; zb = zbuf; bytes_per_row = bpr; width = w; height = h; } /** * Apply atmospheric haze to rendered supplied colour */ void renderer::apply_haze(uint8* r, uint8* g, uint8* b, float depth) { assert(0); float ratio = -depth/256.0; #if 1 // Modify as per preferences ratio *= haze_intensity; ratio -= 0.1; if (ratio > 1.0) { ratio = 1.0; } if (ratio < 0.0) { ratio = 0.0; } #endif *r = uint8((1.0-ratio)* *r + (ratio)*haze_r); *g = uint8((1.0-ratio)* *g + (ratio)*haze_g); *b = uint8((1.0-ratio)* *b + (ratio)*haze_b); } /** * Apply atmospheric haze to rendered supplied colour * XXX lame, do more accurate haze later */ void renderer::apply_haze(fcolour& c, float depth) { float ratio = -depth/256.0; // Modify as per preferences ratio *= haze_intensity; ratio -= 0.1; if (ratio > 1.0) { ratio = 1.0; } if (ratio < 0.0) { ratio = 0.0; } c = c*(1-ratio) + haze*ratio; } /** * Calculate extents rectangle of triangle 'idx' */ void renderer::get_extents(int idx, float* x1, float* x2, float* y1, float* y2) { const triangle3d& tri = tri3d[idx]; int p1 = tri.point[0]; int p2 = tri.point[1]; int p3 = tri.point[2]; float smallest_x = pnt2d[p1].x; float largest_x = pnt2d[p1].x; float smallest_y = pnt2d[p1].y; float largest_y = pnt2d[p1].y; // Find smallest x if (pnt2d[p2].x < smallest_x) { smallest_x = pnt2d[p2].x; } if (pnt2d[p3].x < smallest_x) { smallest_x = pnt2d[p3].x; } // Find largest x if (pnt2d[p2].x > largest_x) { largest_x = pnt2d[p2].x; } if (pnt2d[p3].x > largest_x) { largest_x = pnt2d[p3].x; } // Find smallest y if (pnt2d[p2].y < smallest_y) { smallest_y = pnt2d[p2].y; } if (pnt2d[p3].y < smallest_y) { smallest_y = pnt2d[p3].y; } // Find largest y if (pnt2d[p2].y > largest_y) { largest_y = pnt2d[p2].y; } if (pnt2d[p3].y > largest_y) { largest_y = pnt2d[p3].y; } *x1 = smallest_x; *x2 = largest_x; *y1 = smallest_y; *y2 = largest_y; } /** * Return true if triangle 'idx' should be rendered */ bool renderer::check_extents(int idx) { bool ret_val = true; float left, right, top, bottom; get_extents(idx, &left, &right, &top, &bottom); // Don't render anything totally off screen if (left > width || right < 0) { ret_val = false; } else if (bottom < 0 || top > height) { ret_val = false; } else { // Leftmost and rightmost strips are special cases if (left_border <= 0) { if (left > right_border) { ret_val = false; } } else if (right_border >= width - (right_border - left_border)) { if (left < left_border) { ret_val = false; } } else { if (left < left_border || left > right_border) { ret_val = false; } } } return ret_val; } /** * Clear buffer to black */ void renderer::clear(void) { uint32* p = fb; for (int y=0; yset_maximum(num_tri); //} // Iterate through triangle array... for (int i=0; i pnt2d[tri3d[i].point[middle]].y) { int temp = top; top = middle; middle = temp; } if (pnt2d[tri3d[i].point[middle]].y > pnt2d[tri3d[i].point[bottom]].y) { int temp = bottom; bottom = middle; middle = temp; } if (pnt2d[tri3d[i].point[top]].y > pnt2d[tri3d[i].point[middle]].y) { int temp = top; top = middle; middle = temp; } assert(pnt2d[tri3d[i].point[top]].y <= pnt2d[tri3d[i].point[middle]].y); assert(pnt2d[tri3d[i].point[middle]].y <= pnt2d[tri3d[i].point[bottom]].y); // Render the triangle in two sections, p1.y to p2.y // and p2.y to p3.y const int idx_top = tri3d[i].point[top]; const int idx_middle = tri3d[i].point[middle]; const int idx_bottom = tri3d[i].point[bottom]; const int x_top = int (pnt2d[idx_top].x + 0.5); const int y_top = int (pnt2d[idx_top].y + 0.5); const int d_top = int (pnt2d[idx_top].depth + 0.5); const int x_middle = int (pnt2d[idx_middle].x + 0.5); const int y_middle = int (pnt2d[idx_middle].y + 0.5); const int d_middle = int (pnt2d[idx_middle].depth + 0.5); const int x_bottom = int (pnt2d[idx_bottom].x + 0.5); const int y_bottom = int (pnt2d[idx_bottom].y + 0.5); const int d_bottom = int (pnt2d[idx_bottom].depth + 0.5); // Reject triangles above or below the display if (y_top > height || y_bottom < 0) { continue; } // Reject triangle either behind us or right under our noses. if (d_top > -0.1 || d_middle > -0.1 || d_bottom > -0.1) { continue; } interpolate_i x_coord1(x_top, x_middle, y_middle - y_top); interpolate_i x_coord2(x_top, x_bottom, y_bottom - y_top); interpolate_f depth1(d_top, d_middle, y_middle - y_top); interpolate_f depth2(d_top, d_bottom, y_bottom - y_top); // // Trace first triangle // for (int y=y_top; y < y_middle; y++) { // Is point on screen? if (y >= 0 && y < height) { table_start[y] = x_coord1.val(); table_end[y] = x_coord2.val(); depth_start[y] = depth1.val(); depth_end[y] = depth2.val(); } x_coord1.next(); x_coord2.next(); depth1.next(); depth2.next(); } // // Draw second triangle // x_coord1.set(x_middle, x_bottom, y_bottom - y_middle); depth1.set(d_middle, d_bottom, y_bottom - y_middle); for (int y=y_middle; y <= y_bottom; y++) { // Is point on screen? if (y >= 0 && y < height) { table_start[y] = x_coord1.val(); table_end[y] = x_coord2.val(); depth_start[y] = depth1.val(); depth_end[y] = depth2.val(); } x_coord1.next(); x_coord2.next(); depth1.next(); depth2.next(); } // Now render the scanlines norm = tri3d[i].normal; // Take default colour from triangle col = tri3d[i].col; //fcolour(r/255.0, g/255.0, b/255.0); float cos_theta; if (lighting) { cos_theta = light.direction.dot_product(norm) / (norm.magnitude()*light.direction.magnitude()); if (cos_theta < 0.0) { cos_theta = 0.0; } // Ambient + directional // cos_theta = ambient_intensity + cos_theta*direct_intensity; if (cos_theta > 1.0) { cos_theta = 1.0; } if (cos_theta < 0.0) { cos_theta = 0.0; } } else { cos_theta = 1.0; } //colour = col; //*cos_theta; #if 0 // Work out reflected colour of directional light col /*our*/ = ambient*col + light.reflected(col) * cos_theta; col.clamp(); //colour.clamp(); #endif if (tri3d[i].get_flag(eSHADOW)) { col = light.reflected(col) * cos_theta; } else { col = ambient*col + light.reflected(col) * cos_theta; } ////col.clamp(); float depth = depth_end[y_top]; if (!blend) { if (fog) { apply_haze(col, depth); } } fcolour::exposure(col, 2); col32 = fcolour::get_rgb32(col); // Now render the scanlines for (int y=y_top; y<=y_bottom; y++) { if (y >=0 && y < height) { int x1 = int (table_start[y] + 0.5); int x2 = int (table_end[y] + 0.5); if (x1 < 0 && x2 < 0) { continue; } if (x1 > width && x2 > width) { continue; } gfx = fb; gfx += (y * bytes_per_row/4); render_scanline(y); } } } } /** * Render scanline 'y' using edge tables */ void renderer::render_scanline(int y) { int x1 = int (table_start[y] + 0.5); int x2 = int (table_end[y] + 0.5); #if 1 float d1, d2; if (x1 < x2) { d1 = depth_start[y]; d2 = depth_end[y]; } else { // Switch so x1 is left of x2 int temp = x1; x1 = x2; x2 = temp; d1 = depth_end[y]; d2 = depth_start[y]; } float depth = d1; float d_inc = 0.0; if (x2 - x1) { d_inc = float(d2 - d1)/(x2 - x1); } #endif ///float depth = depth_end[y]; //gfx += (y * bytes_per_row/4); float* p = zb->get_buffer(y, x1); //fcolour colour = col; for (int x=x1; x<=x2; x++) { if (x >= 0 && x < width) { if (depth > *p) { *p = depth; // if (!blend) { // if (fog) { // apply_haze(colour, depth); // } gfx[x] = col32; #if 0 } else { uint32 val = gfx[x]; fcolour current(val); float ratio = colour.r; current = current*(1-ratio) + colour*ratio; if (fog) { apply_haze(colour, depth); } //current = current*cos_theta; gfx[x] = fcolour::make_rgb32(colour); } #endif } } p++; depth += d_inc; } }