Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <utils/soft_rend.hpp>
- //todo: do near and far clipping stuff for soft_rend
- //note: don't clear filled edge buffer between calls to drawTriangles()
- //note: scanlines of length 1 may or may not need to be trimmed after all
- //note: switch iround back to i32 (truncation) if off-by-one errors start to occur
- #define toi32 iround
- //returns the middle part of x(y) = x0 + ((x1-x0)/(y1-y0)) * (y - y0)
- static inline fx16_8 _getInterpConstant(fx_vec2& p0, fx_vec2& p1){
- fx16_8 divisor = p1.y-p0.y;
- if(divisor == 0) divisor = fx_epsilon; //prevents divide-by-zero
- return (p1.x-p0.x)/divisor;
- }
- #define _INTERP(_y, _p0, _con) ( (_p0).x + (_con) * ((_y)-(_p0.y)) )
- #define _INTERP_AC(_y) _INTERP(_y, a, aci)
- #define _INTERP_AB(_y) _INTERP(_y, a, abi)
- #define _INTERP_BC(_y) _INTERP(_y, b, bci)
- void soft_rend::_interpolateTriangle(const soft_tri& tri,
- const std::vector<fx_vec3>& verts)
- {
- int numRows = _row_len = 0; //initialize number of rows to 0
- SDL_Point logicalSize = _logicalSize; //for screen boundary comparisons
- //z isn't needed here, so convert to vec2
- //(also align to pixel boundaries)
- fx_vec2 a = fx_vec3_2(verts.at(tri.a)).round();
- fx_vec2 b = fx_vec3_2(verts.at(tri.b)).round();
- fx_vec2 c = fx_vec3_2(verts.at(tri.c)).round();
- //sort points vertically, so that a & c are top and bottom respectively
- if(a.y > c.y) std::swap(a,c);
- if(a.y > b.y) std::swap(a,b);
- if(b.y > c.y) std::swap(b,c);
- //return early if triangle's points are completely off-screen horizontally
- if( (a.x < 0 && b.x < 0 && c.x < 0) ||
- (a.x>=logicalSize.x && b.x>=logicalSize.x && c.x>=logicalSize.x) )
- {
- return;
- }
- //return early if triangle's points are completely off-screen vertically
- //(points are sorted vertically, so only c.y and a.y need to be checked)
- if(c.y < 0 || a.y >= logicalSize.y) return;
- //'is point b on the left or right side of line ac (as it appears on-screen)?'
- bool isRightBend = fx_vec2_cross(a,b,c) > 0; //cross as in cross product
- //precalculate interpolation constants
- const fx16_8 aci = _getInterpConstant(a,c);
- const fx16_8 abi = _getInterpConstant(a,b);
- const fx16_8 bci = _getInterpConstant(b,c);
- //interpolate triangle edges
- if(a.y == c.y){ //triangle is a single scanline tall (just a horiz. line!)
- int a_x = a.x.toi32();
- int a_y = a.y.toi32();
- int c_x = c.x.toi32();
- if(!_edge_filled[a_y]){
- _row_start[numRows].x = SDL_min(a_x,c_x);
- _row_start[numRows].y = a_y;
- _row_end [numRows].x = SDL_max(a_x,c_x);
- numRows = (_row_start[numRows].x < logicalSize.x) &&
- (_row_end [numRows].x >= 0);
- }
- } else { //triangle is >1 scanline(s) tall
- SDL_Point* row_ac, *row_abc;
- if(isRightBend){ //line ac is edge start
- row_ac = _row_start;
- row_abc = _row_end;
- } else { //line ac is edge end (so swap start and end)
- row_ac = _row_end;
- row_abc = _row_start;
- }
- if(b.y > 0){ //(if b.y < 1, that means nothing from a to b would render)
- fx16_8 ab_max = SDL_min(b.y,logicalSize.y);
- for(fx16_8 yi=SDL_max(a.y,0); yi<ab_max; ++yi){
- int yi_i32 = yi.toi32();
- if(_edge_filled[yi_i32]) continue; //if no space is left on current scanline
- _row_start[numRows].y = yi_i32; //_row_start accessed directly for y
- //a -> c, a -> b
- row_ac[numRows].x = _INTERP_AC(yi).toi32();
- row_abc[numRows].x = _INTERP_AB(yi).toi32();
- if(_row_start[numRows].x >= logicalSize.x) continue; //if start is past screen
- if(_row_end[numRows].x < 0) continue; //if end is before screen
- ++numRows; //only increment numRows if scanline is valid to draw on
- }
- }
- if(b.y < logicalSize.y){ //if b.y >= logicalSize.y, everything from b to c is hidden
- fx16_8 bc_max = SDL_min(c.y+1,logicalSize.y); //c.y+1 so the for-loop includes c.y
- for(fx16_8 yi=SDL_max(b.y,0); yi<bc_max; ++yi){
- int yi_i32 = yi.toi32();
- if(_edge_filled[yi_i32]) continue; //if no space is left on current scanline
- _row_start[numRows].y = yi_i32; //_row_start accessed directly for y
- //a -> c, b -> c
- row_ac[numRows].x = _INTERP_AC(yi).toi32();
- row_abc[numRows].x = _INTERP_BC(yi).toi32();
- if(_row_start[numRows].x >= logicalSize.x) continue; //if start is past screen
- if(_row_end[numRows].x < 0) continue; //if end is before screen
- ++numRows; //only increment numRows if scanline is valid to draw on
- }
- }
- }
- //(trimming the south-east edge should prevent overdraw)
- //trim south edge unless c.y is off-screen
- if(c.y < logicalSize.y) numRows = SDL_max(numRows-1, 1);
- //clip triangle width to target screen space, and trim east edge
- for(int row=0; row<numRows; ++row){
- _row_start[row].x = SDL_max(_row_start[row].x, 0);
- _row_end [row].x = SDL_min(_row_end [row].x-1, logicalSize.x-1);
- }
- _row_len = numRows;
- }
- void soft_rend::drawTriangle(const soft_tri& tri,
- const std::vector<fx_vec3>& verts)
- {
- _interpolateTriangle(tri,verts);
- int width = _logicalSize.x;
- int numRows = _row_len;
- soft_color* pixels = (soft_color*)_target->pixels;
- soft_color drawColor = tri.color;
- if(drawColor.v == UNKNOWN_COLOR) drawColor = _drawColor;
- for(int row=0; row<numRows; ++row){
- if(_row_start[row].y<0) SDL_Log("y<0");
- if(_row_start[row].y>=_logicalSize.y) SDL_Log("y>=w");
- int y_position = _row_start[row].y*width;
- int start = _row_start[row].x + y_position;
- int end = _row_end [row].x + y_position;
- for(int i=start; i<=end; ++i) pixels[i].v = drawColor.v;
- }
- }
- void soft_rend::drawTriangles(const std::vector<soft_tri>& tris,
- const std::vector<fx_vec3>& verts)
- {
- int width = _logicalSize.x;
- soft_color* pixels = (soft_color*)_target->pixels;
- for(const soft_tri& tri : tris){
- _interpolateTriangle(tri,verts);
- int numRows = _row_len;
- soft_color drawColor = tri.color;
- if(drawColor.v == UNKNOWN_COLOR) drawColor = _drawColor;
- for(int row=0; row<numRows; ++row){
- int y_position = _row_start[row].y*width;
- int start = _row_start[row].x + y_position;
- int end = _row_end [row].x + y_position;
- for(int i=start; i<=end; ++i) pixels[i].v = drawColor.v;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement