Advertisement
Kitomas

soft_rendTri.cpp

Dec 1st, 2023
662
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.42 KB | None | 0 0
  1. #include <utils/soft_rend.hpp>
  2. //todo: do near and far clipping stuff for soft_rend
  3. //note: don't clear filled edge buffer between calls to drawTriangles()
  4. //note: scanlines of length 1 may or may not need to be trimmed after all
  5. //note: switch iround back to i32 (truncation) if off-by-one errors start to occur
  6. #define toi32 iround
  7.  
  8.  
  9. //returns the middle part of x(y) = x0 + ((x1-x0)/(y1-y0)) * (y - y0)
  10. static inline fx16_8 _getInterpConstant(fx_vec2& p0, fx_vec2& p1){
  11.   fx16_8 divisor = p1.y-p0.y;
  12.   if(divisor == 0) divisor = fx_epsilon; //prevents divide-by-zero
  13.   return (p1.x-p0.x)/divisor;
  14. }
  15.  
  16. #define _INTERP(_y, _p0, _con) ( (_p0).x + (_con) * ((_y)-(_p0.y)) )
  17. #define _INTERP_AC(_y) _INTERP(_y, a, aci)
  18. #define _INTERP_AB(_y) _INTERP(_y, a, abi)
  19. #define _INTERP_BC(_y) _INTERP(_y, b, bci)
  20.  
  21.  
  22.  
  23.  
  24. void soft_rend::_interpolateTriangle(const soft_tri& tri,
  25.                                      const std::vector<fx_vec3>& verts)
  26. {
  27.   int numRows = _row_len = 0; //initialize number of rows to 0
  28.   SDL_Point logicalSize = _logicalSize; //for screen boundary comparisons
  29.  
  30.   //z isn't needed here, so convert to vec2
  31.    //(also align to pixel boundaries)
  32.   fx_vec2 a = fx_vec3_2(verts.at(tri.a)).round();
  33.   fx_vec2 b = fx_vec3_2(verts.at(tri.b)).round();
  34.   fx_vec2 c = fx_vec3_2(verts.at(tri.c)).round();
  35.  
  36.   //sort points vertically, so that a & c are top and bottom respectively
  37.   if(a.y > c.y) std::swap(a,c);
  38.   if(a.y > b.y) std::swap(a,b);
  39.   if(b.y > c.y) std::swap(b,c);
  40.  
  41.   //return early if triangle's points are completely off-screen horizontally
  42.   if( (a.x < 0  &&  b.x < 0  &&  c.x < 0) ||
  43.       (a.x>=logicalSize.x && b.x>=logicalSize.x && c.x>=logicalSize.x) )
  44.   {
  45.     return;
  46.   }
  47.  
  48.   //return early if triangle's points are completely off-screen vertically
  49.    //(points are sorted vertically, so only c.y and a.y need to be checked)
  50.   if(c.y < 0  ||  a.y >= logicalSize.y) return;
  51.  
  52.   //'is point b on the left or right side of line ac (as it appears on-screen)?'
  53.   bool isRightBend = fx_vec2_cross(a,b,c) > 0; //cross as in cross product
  54.  
  55.   //precalculate interpolation constants
  56.   const fx16_8 aci = _getInterpConstant(a,c);
  57.   const fx16_8 abi = _getInterpConstant(a,b);
  58.   const fx16_8 bci = _getInterpConstant(b,c);
  59.  
  60.  
  61.   //interpolate triangle edges
  62.   if(a.y == c.y){ //triangle is a single scanline tall (just a horiz. line!)
  63.     int a_x = a.x.toi32();
  64.     int a_y = a.y.toi32();
  65.     int c_x = c.x.toi32();
  66.     if(!_edge_filled[a_y]){
  67.       _row_start[numRows].x = SDL_min(a_x,c_x);
  68.       _row_start[numRows].y = a_y;
  69.       _row_end  [numRows].x = SDL_max(a_x,c_x);
  70.       numRows = (_row_start[numRows].x < logicalSize.x) &&
  71.                 (_row_end  [numRows].x >= 0);
  72.     }
  73.  
  74.  
  75.   } else { //triangle is >1 scanline(s) tall
  76.     SDL_Point* row_ac, *row_abc;
  77.     if(isRightBend){ //line ac is edge start
  78.       row_ac  = _row_start;
  79.       row_abc = _row_end;
  80.     } else { //line ac is edge end (so swap start and end)
  81.       row_ac  = _row_end;
  82.       row_abc = _row_start;
  83.     }
  84.  
  85.     if(b.y > 0){ //(if b.y < 1, that means nothing from a to b would render)
  86.       fx16_8 ab_max = SDL_min(b.y,logicalSize.y);
  87.       for(fx16_8 yi=SDL_max(a.y,0); yi<ab_max; ++yi){
  88.         int yi_i32 = yi.toi32();
  89.         if(_edge_filled[yi_i32]) continue; //if no space is left on current scanline
  90.         _row_start[numRows].y = yi_i32; //_row_start accessed directly for y
  91.         //a -> c,  a -> b
  92.         row_ac[numRows].x = _INTERP_AC(yi).toi32();
  93.         row_abc[numRows].x = _INTERP_AB(yi).toi32();
  94.         if(_row_start[numRows].x >= logicalSize.x) continue; //if start is past screen
  95.         if(_row_end[numRows].x < 0) continue; //if end is before screen
  96.         ++numRows; //only increment numRows if scanline is valid to draw on
  97.       }
  98.     }
  99.  
  100.     if(b.y < logicalSize.y){ //if b.y >= logicalSize.y, everything from b to c is hidden
  101.       fx16_8 bc_max = SDL_min(c.y+1,logicalSize.y); //c.y+1 so the for-loop includes c.y
  102.       for(fx16_8 yi=SDL_max(b.y,0); yi<bc_max; ++yi){
  103.         int yi_i32 = yi.toi32();
  104.         if(_edge_filled[yi_i32]) continue; //if no space is left on current scanline
  105.         _row_start[numRows].y = yi_i32; //_row_start accessed directly for y
  106.         //a -> c,  b -> c
  107.         row_ac[numRows].x = _INTERP_AC(yi).toi32();
  108.         row_abc[numRows].x   = _INTERP_BC(yi).toi32();
  109.         if(_row_start[numRows].x >= logicalSize.x) continue; //if start is past screen
  110.         if(_row_end[numRows].x < 0) continue; //if end is before screen
  111.         ++numRows; //only increment numRows if scanline is valid to draw on
  112.       }
  113.     }
  114.   }
  115.  
  116.  
  117.   //(trimming the south-east edge should prevent overdraw)
  118.    //trim south edge unless c.y is off-screen
  119.   if(c.y < logicalSize.y) numRows = SDL_max(numRows-1, 1);
  120.  
  121.   //clip triangle width to target screen space, and trim east edge
  122.   for(int row=0; row<numRows; ++row){
  123.     _row_start[row].x = SDL_max(_row_start[row].x,   0);
  124.     _row_end  [row].x = SDL_min(_row_end  [row].x-1, logicalSize.x-1);
  125.   }
  126.  
  127.   _row_len = numRows;
  128.  
  129. }
  130.  
  131.  
  132.  
  133.  
  134. void soft_rend::drawTriangle(const soft_tri& tri,
  135.                              const std::vector<fx_vec3>& verts)
  136. {
  137.   _interpolateTriangle(tri,verts);
  138.  
  139.   int width   = _logicalSize.x;
  140.   int numRows = _row_len;
  141.  
  142.   soft_color* pixels = (soft_color*)_target->pixels;
  143.   soft_color drawColor = tri.color;
  144.   if(drawColor.v == UNKNOWN_COLOR) drawColor = _drawColor;
  145.  
  146.   for(int row=0; row<numRows; ++row){
  147.     if(_row_start[row].y<0) SDL_Log("y<0");
  148.     if(_row_start[row].y>=_logicalSize.y) SDL_Log("y>=w");
  149.  
  150.     int y_position = _row_start[row].y*width;
  151.     int start = _row_start[row].x + y_position;
  152.     int end   = _row_end  [row].x + y_position;
  153.     for(int i=start; i<=end; ++i) pixels[i].v = drawColor.v;
  154.   }
  155. }
  156.  
  157.  
  158.  
  159.  
  160. void soft_rend::drawTriangles(const std::vector<soft_tri>& tris,
  161.                               const std::vector<fx_vec3>& verts)
  162. {
  163.   int width = _logicalSize.x;
  164.   soft_color* pixels = (soft_color*)_target->pixels;
  165.  
  166.  
  167.   for(const soft_tri& tri : tris){
  168.     _interpolateTriangle(tri,verts);
  169.  
  170.     int numRows = _row_len;
  171.  
  172.     soft_color drawColor = tri.color;
  173.     if(drawColor.v == UNKNOWN_COLOR) drawColor = _drawColor;
  174.  
  175.     for(int row=0; row<numRows; ++row){
  176.       int y_position = _row_start[row].y*width;
  177.       int start = _row_start[row].x + y_position;
  178.       int end   = _row_end  [row].x + y_position;
  179.       for(int i=start; i<=end; ++i) pixels[i].v = drawColor.v;
  180.     }
  181.   }
  182. }
  183.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement