Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Start of conversion to new texture mapper.
  23.  *
  24.  */
  25.  
  26. #include "pstypes.h"
  27. #include "maths.h"
  28. #include "vecmat.h"
  29. #include "gr.h"
  30. #include "3d.h"
  31. #include "dxxerror.h"
  32. #include "render.h"
  33. #include "texmap.h"
  34. #include "texmapl.h"
  35. #include "rle.h"
  36. #include "scanline.h"
  37. #include "u_mem.h"
  38.  
  39. #include "dxxsconf.h"
  40. #include "dsx-ns.h"
  41. #include <utility>
  42.  
  43. namespace dcx {
  44.  
  45. #if DXX_USE_EDITOR
  46. #define EDITOR_TMAP 1       //if in, include extra stuff
  47. #endif
  48.  
  49. // Temporary texture map, interface from Matt's 3d system to Mike's texture mapper.
  50.  
  51. int     Lighting_on=1;                  // initialize to no lighting
  52. unsigned        Current_seg_depth;              // HACK INTERFACE: how far away the current segment (& thus texture) is
  53.  
  54. // These variables are the interface to assembler.  They get set for each texture map, which is a real waste of time.
  55. //      They should be set only when they change, which is generally when the window bounds change.  And, even still, it's
  56. //      a pretty bad interface.
  57. int     bytes_per_row=-1;
  58. unsigned char *write_buffer;
  59.  
  60. fix fx_l, fx_u, fx_v, fx_z, fx_du_dx, fx_dv_dx, fx_dz_dx, fx_dl_dx;
  61. int fx_xleft, fx_xright, fx_y;
  62. const unsigned char *pixptr;
  63. uint8_t Transparency_on = 0;
  64. uint8_t tmap_flat_color;
  65.  
  66. int     Interpolation_method;   // 0 = choose best method
  67. // -------------------------------------------------------------------------------------
  68. template <std::size_t... N>
  69. static inline constexpr const std::array<fix, 1 + sizeof...(N)> init_fix_recip_table(std::index_sequence<0, N...>)
  70. {
  71.         /* gcc 4.5 fails on bare initializer list */
  72.         return std::array<fix, 1 + sizeof...(N)>{{F1_0, (F1_0 / N)...}};
  73. }
  74.  
  75. constexpr std::array<fix, FIX_RECIP_TABLE_SIZE> fix_recip_table = init_fix_recip_table(std::make_index_sequence<FIX_RECIP_TABLE_SIZE>());
  76.  
  77. // -------------------------------------------------------------------------------------
  78. //      Initialize interface variables to assembler.
  79. //      These things used to be constants.  This routine is now (10/6/93) getting called for
  80. //      every texture map.  It should get called whenever the window changes, or, preferably,
  81. //      not at all.  I'm pretty sure these variables are only being used for range checking.
  82. void init_interface_vars_to_assembler(void)
  83. {
  84.         grs_bitmap      *bp;
  85.         bp = &grd_curcanv->cv_bitmap;
  86.  
  87.         Assert(bp!=NULL);
  88.         Assert(bp->bm_data!=NULL);
  89.         //      If bytes_per_row has changed, create new table of pointers.
  90.         if (bytes_per_row != static_cast<int>(bp->bm_rowsize)) {
  91.                 bytes_per_row = static_cast<int>(bp->bm_rowsize);
  92.         }
  93.  
  94.         write_buffer = bp->bm_mdata;
  95.  
  96.         Window_clip_left = 0;
  97.         Window_clip_right = static_cast<int>(bp->bm_w)-1;
  98.         Window_clip_top = 0;
  99.         Window_clip_bot = static_cast<int>(bp->bm_h)-1;
  100. }
  101.  
  102. static int Lighting_enabled;
  103. // -------------------------------------------------------------------------------------
  104. //                             VARIABLES
  105.  
  106. // -------------------------------------------------------------------------------------
  107. //      Returns number preceding val modulo modulus.
  108. //      prevmod(3,4) = 2
  109. //      prevmod(0,4) = 3
  110. int prevmod(int val,int modulus)
  111. {
  112.         if (val > 0)
  113.                 return val-1;
  114.         else
  115.                 return modulus-1;
  116. //      return (val + modulus - 1) % modulus;
  117. }
  118.  
  119.  
  120. //      Returns number succeeding val modulo modulus.
  121. //      succmod(3,4) = 0
  122. //      succmod(0,4) = 1
  123. int succmod(int val,int modulus)
  124. {
  125.         if (val < modulus-1)
  126.                 return val+1;
  127.         else
  128.                 return 0;
  129.  
  130. //      return (val + 1) % modulus;
  131. }
  132.  
  133. // -------------------------------------------------------------------------------------
  134. //      Select topmost vertex (minimum y coordinate) and bottommost (maximum y coordinate) in
  135. //      texture map.  If either is part of a horizontal edge, then select leftmost vertex for
  136. //      top, rightmost vertex for bottom.
  137. //      Important: Vertex is selected with integer precision.  So, if there are vertices at
  138. //      (0.0,0.7) and (0.5,0.3), the first vertex is selected, because they y coordinates are
  139. //      considered the same, so the smaller x is favored.
  140. //      Parameters:
  141. //              nv              number of vertices
  142. //              v3d     pointer to 3d vertices containing u,v,x2d,y2d coordinates
  143. //      Results in:
  144. //              *min_y_ind
  145. //              *max_y_ind
  146. // -------------------------------------------------------------------------------------
  147. void compute_y_bounds(const g3ds_tmap &t, int &vlt, int &vlb, int &vrt, int &vrb,int &bottom_y_ind)
  148. {
  149.         int     min_y,max_y;
  150.         int     min_y_ind;
  151.         int     original_vrt;
  152.         fix     min_x;
  153.  
  154.         // Scan all vertices, set min_y_ind to vertex with smallest y coordinate.
  155.         min_y = f2i(t.verts[0].y2d);
  156.         max_y = min_y;
  157.         min_y_ind = 0;
  158.         min_x = f2i(t.verts[0].x2d);
  159.         bottom_y_ind = 0;
  160.  
  161.         for (int i=1; i<t.nv; i++) {
  162.                 if (f2i(t.verts[i].y2d) < min_y) {
  163.                         min_y = f2i(t.verts[i].y2d);
  164.                         min_y_ind = i;
  165.                         min_x = f2i(t.verts[i].x2d);
  166.                 } else if (f2i(t.verts[i].y2d) == min_y) {
  167.                         if (f2i(t.verts[i].x2d) < min_x) {
  168.                                 min_y_ind = i;
  169.                                 min_x = f2i(t.verts[i].x2d);
  170.                         }
  171.                 }
  172.                 if (f2i(t.verts[i].y2d) > max_y) {
  173.                         max_y = f2i(t.verts[i].y2d);
  174.                         bottom_y_ind = i;
  175.                 }
  176.         }
  177.  
  178. //--removed mk, 11/27/94--      //      Check for a non-upright-hourglass polygon and fix, if necessary, by bashing a y coordinate.
  179. //--removed mk, 11/27/94--      //      min_y_ind = index of minimum y coordinate, *bottom_y_ind = index of maximum y coordinate
  180. //--removed mk, 11/27/94--{
  181. //--removed mk, 11/27/94--      int     max_temp, min_temp;
  182. //--removed mk, 11/27/94--
  183. //--removed mk, 11/27/94--      max_temp = *bottom_y_ind;
  184. //--removed mk, 11/27/94--      if (*bottom_y_ind < min_y_ind)
  185. //--removed mk, 11/27/94--              max_temp += t->nv;
  186. //--removed mk, 11/27/94--
  187. //--removed mk, 11/27/94--      for (i=min_y_ind; i<max_temp; i++) {
  188. //--removed mk, 11/27/94--              if (f2i(t->verts[i%t->nv].y2d) > f2i(t->verts[(i+1)%t->nv].y2d)) {
  189. //--removed mk, 11/27/94--                      Int3();
  190. //--removed mk, 11/27/94--                      t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d;
  191. //--removed mk, 11/27/94--              }
  192. //--removed mk, 11/27/94--      }
  193. //--removed mk, 11/27/94--
  194. //--removed mk, 11/27/94--      min_temp = min_y_ind;
  195. //--removed mk, 11/27/94--      if (min_y_ind < *bottom_y_ind)
  196. //--removed mk, 11/27/94--              min_temp += t->nv;
  197. //--removed mk, 11/27/94--
  198. //--removed mk, 11/27/94--      for (i=*bottom_y_ind; i<min_temp; i++) {
  199. //--removed mk, 11/27/94--              if (f2i(t->verts[i%t->nv].y2d) < f2i(t->verts[(i+1)%t->nv].y2d)) {
  200. //--removed mk, 11/27/94--                      Int3();
  201. //--removed mk, 11/27/94--                      t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d;
  202. //--removed mk, 11/27/94--              }
  203. //--removed mk, 11/27/94--      }
  204. //--removed mk, 11/27/94--}
  205.  
  206.         // Set "vertex left top", etc. based on vertex with topmost y coordinate
  207.         vlb = prevmod(vlt = min_y_ind,t.nv);
  208.         vrb = succmod(vrt = vlt,t.nv);
  209.  
  210.         // If right edge is horizontal, then advance along polygon bound until it no longer is or until all
  211.         // vertices have been examined.
  212.         // (Left edge cannot be horizontal, because *vlt is set to leftmost point with highest y coordinate.)
  213.  
  214.         original_vrt = vrt;
  215.  
  216.         while (f2i(t.verts[vrt].y2d) == f2i(t.verts[vrb].y2d)) {
  217.                 if (succmod(vrt,t.nv) == original_vrt) {
  218.                         break;
  219.                 }
  220.                 vrt = succmod(vrt,t.nv);
  221.                 vrb = succmod(vrt,t.nv);
  222.         }
  223. }
  224.  
  225. // -------------------------------------------------------------------------------------
  226. //      Returns dx/dy given two vertices.
  227. //      If dy == 0, returns 0.0
  228. // -------------------------------------------------------------------------------------
  229. //--fix compute_dx_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex)
  230. //--{
  231. //--    int     dy;
  232. //--
  233. //--    // compute delta x with respect to y for any edge
  234. //--    dy = f2i(t->verts[bottom_vertex].y2d - t->verts[top_vertex].y2d) + 1;
  235. //--    if (dy)
  236. //--            return (t->verts[bottom_vertex].x2d - t->verts[top_vertex].x2d) / dy;
  237. //--    else
  238. //--            return 0;
  239. //--
  240. //--}
  241.  
  242. //#if !DXX_USE_OGL
  243. static fix compute_du_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  244. {
  245.         return fixmul(t.verts[bottom_vertex].u - t.verts[top_vertex].u, recip_dy);
  246. }
  247.  
  248.  
  249. static fix compute_dv_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  250. {
  251.         return fixmul(t.verts[bottom_vertex].v - t.verts[top_vertex].v, recip_dy);
  252. }
  253.  
  254. static fix compute_dl_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  255. {
  256.         return fixmul(t.verts[bottom_vertex].l - t.verts[top_vertex].l, recip_dy);
  257. }
  258.  
  259. fix compute_dx_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  260. {
  261.         return fixmul(t.verts[bottom_vertex].x2d - t.verts[top_vertex].x2d, recip_dy);
  262. }
  263.  
  264. static fix compute_du_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  265. {
  266.         return fixmul(fixmul(t.verts[bottom_vertex].u,t.verts[bottom_vertex].z) - fixmul(t.verts[top_vertex].u,t.verts[top_vertex].z), recip_dy);
  267. }
  268.  
  269. static fix compute_dv_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  270. {
  271.         return fixmul(fixmul(t.verts[bottom_vertex].v,t.verts[bottom_vertex].z) - fixmul(t.verts[top_vertex].v,t.verts[top_vertex].z), recip_dy);
  272. }
  273.  
  274. static fix compute_dz_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy)
  275. {
  276.         return fixmul(t.verts[bottom_vertex].z - t.verts[top_vertex].z, recip_dy);
  277.  
  278. }
  279.  
  280. // -------------------------------------------------------------------------------------
  281. //      Texture map current scanline in perspective.
  282. // -------------------------------------------------------------------------------------
  283. static void ntmap_scanline_lighted(const grs_bitmap &srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix zleft, fix zright, fix lleft, fix lright)
  284. {
  285.         fix     dx,recip_dx;
  286.  
  287.         fx_xright = f2i(xright);
  288.         //edited 06/27/99 Matt Mueller - moved these tests up from within the switch so as not to do a bunch of needless calculations when we are just gonna return anyway.  Slight fps boost?
  289.         if (fx_xright < Window_clip_left)
  290.                 return;
  291.         fx_xleft = f2i(xleft);
  292.         if (fx_xleft > Window_clip_right)
  293.                 return;
  294.         //end edit -MM
  295.  
  296.         dx = fx_xright - fx_xleft;
  297.         if ((dx < 0) || (xright < 0) || (xleft > xright))               // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers
  298.                 return;
  299.  
  300.         // setup to call assembler scanline renderer
  301.         recip_dx = fix_recip(dx);
  302.  
  303.         fx_u = uleft;
  304.         fx_v = vleft;
  305.         fx_z = zleft;
  306.        
  307.         fx_du_dx = fixmul(uright - uleft,recip_dx);
  308.         fx_dv_dx = fixmul(vright - vleft,recip_dx);
  309.         fx_dz_dx = fixmul(zright - zleft,recip_dx);
  310.         fx_y = y;
  311.         pixptr = srcb.bm_data;
  312.  
  313.         switch (Lighting_enabled) {
  314.                 case 0:
  315.                         //added 05/17/99 Matt Mueller - prevent writing before the buffer
  316.             if ((fx_y == 0) && (fx_xleft < 0))
  317.                                 fx_xleft = 0;
  318.                         //end addition -MM
  319.                         if (fx_xright > Window_clip_right)
  320.                                 fx_xright = Window_clip_right;
  321.                        
  322.                         cur_tmap_scanline_per();
  323.                         break;
  324.                 case 1: {
  325.                         fix     mul_thing;
  326.  
  327.                         if (lleft < 0) lleft = 0;
  328.                         if (lright < 0) lright = 0;
  329.                         if (lleft > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lleft = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2);
  330.                         if (lright > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lright = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2);
  331.  
  332.                         fx_l = lleft;
  333.                         fx_dl_dx = fixmul(lright - lleft,recip_dx);
  334.  
  335.                         //      This is a pretty ugly hack to prevent lighting overflows.
  336.                         mul_thing = dx * fx_dl_dx;
  337.                         if (lleft + mul_thing < 0)
  338.                                 fx_dl_dx += 12;
  339.                         else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2))
  340.                                 fx_dl_dx -= 12;
  341.  
  342.                         //added 05/17/99 Matt Mueller - prevent writing before the buffer
  343.             if ((fx_y == 0) && (fx_xleft < 0))
  344.                                 fx_xleft = 0;
  345.                         //end addition -MM
  346.                         if (fx_xright > Window_clip_right)
  347.                                 fx_xright = Window_clip_right;
  348.  
  349.                         cur_tmap_scanline_per();
  350.                         break;
  351.                 }
  352.                 case 2:
  353. #ifdef EDITOR_TMAP
  354.                         fx_xright = f2i(xright);
  355.                         fx_xleft = f2i(xleft);
  356.  
  357.                         tmap_flat_color = 1;
  358.                         cur_tmap_scanline_flat();
  359. #else
  360.                         Int3(); //      Illegal, called an editor only routine!
  361. #endif
  362.                         break;
  363.         }
  364.  
  365. }
  366.  
  367. // -------------------------------------------------------------------------------------
  368. //      Render a texture map with lighting using perspective interpolation in inner and outer loops.
  369. // -------------------------------------------------------------------------------------
  370. static void ntexture_map_lighted(const grs_bitmap &srcb, const g3ds_tmap &t)
  371. {
  372.         int     vlt,vrt,vlb,vrb;        // vertex left top, vertex right top, vertex left bottom, vertex right bottom
  373.         int     topy,boty,dy;
  374.         fix     dx_dy_left,dx_dy_right;
  375.         fix     du_dy_left,du_dy_right;
  376.         fix     dv_dy_left,dv_dy_right;
  377.         fix     dz_dy_left,dz_dy_right;
  378.         fix     dl_dy_left,dl_dy_right;
  379.         fix     recip_dyl, recip_dyr;
  380.         int     max_y_vertex;
  381.         fix     xleft,xright,uleft,vleft,uright,vright,zleft,zright,lleft,lright;
  382.         int     next_break_left, next_break_right;
  383.  
  384.         //remove stupid warnings in compile
  385.         dl_dy_left = F1_0;
  386.         dl_dy_right = F1_0;
  387.         lleft = F1_0;
  388.         lright = F1_0;
  389.  
  390.         auto &v3d = t.verts;
  391.  
  392.         // Determine top and bottom y coords.
  393.         compute_y_bounds(t,vlt,vlb,vrt,vrb,max_y_vertex);
  394.  
  395.         // Set top and bottom (of entire texture map) y coordinates.
  396.         topy = f2i(v3d[vlt].y2d);
  397.         boty = f2i(v3d[max_y_vertex].y2d);
  398.         if (topy > Window_clip_bot)
  399.                 return;
  400.         if (boty > Window_clip_bot)
  401.                 boty = Window_clip_bot;
  402.  
  403.         // Set amount to change x coordinate for each advance to next scanline.
  404.         dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
  405.         recip_dyl = fix_recip(dy);
  406.  
  407.         dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl);
  408.         du_dy_left = compute_du_dy(t,vlt,vlb, recip_dyl);
  409.         dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dyl);
  410.         dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dyl);
  411.  
  412.         dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
  413.         recip_dyr = fix_recip(dy);
  414.  
  415.         du_dy_right = compute_du_dy(t,vrt,vrb, recip_dyr);
  416.         dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr);
  417.         dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dyr);
  418.         dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dyr);
  419.  
  420.         if (Lighting_enabled) {
  421.                 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl);
  422.                 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr);
  423.  
  424.                 lleft = v3d[vlt].l;
  425.                 lright = v3d[vrt].l;
  426.         }
  427.  
  428.         // Set initial values for x, u, v
  429.         xleft = v3d[vlt].x2d;
  430.         xright = v3d[vrt].x2d;
  431.  
  432.         zleft = v3d[vlt].z;
  433.         zright = v3d[vrt].z;
  434.  
  435.         uleft = fixmul(v3d[vlt].u,zleft);
  436.         uright = fixmul(v3d[vrt].u,zright);
  437.         vleft = fixmul(v3d[vlt].v,zleft);
  438.         vright = fixmul(v3d[vrt].v,zright);
  439.  
  440.         // scan all rows in texture map from top through first break.
  441.         next_break_left = f2i(v3d[vlb].y2d);
  442.         next_break_right = f2i(v3d[vrb].y2d);
  443.  
  444.         for (int y = topy; y < boty; y++) {
  445.  
  446.                 // See if we have reached the end of the current left edge, and if so, set
  447.                 // new values for dx_dy and x,u,v
  448.                 if (y == next_break_left) {
  449.                         fix     recip_dy;
  450.  
  451.                         // Handle problem of double points.  Search until y coord is different.  Cannot get
  452.                         // hung in an infinite loop because we know there is a vertex with a lower y coordinate
  453.                         // because in the for loop, we don't scan all spanlines.
  454.                         while (y == f2i(v3d[vlb].y2d)) {
  455.                                 vlt = vlb;
  456.                                 vlb = prevmod(vlb,t.nv);
  457.                         }
  458.                         next_break_left = f2i(v3d[vlb].y2d);
  459.  
  460.                         dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
  461.                         recip_dy = fix_recip(dy);
  462.  
  463.                         dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy);
  464.  
  465.                         xleft = v3d[vlt].x2d;
  466.                         zleft = v3d[vlt].z;
  467.                         uleft = fixmul(v3d[vlt].u,zleft);
  468.                         vleft = fixmul(v3d[vlt].v,zleft);
  469.                         lleft = v3d[vlt].l;
  470.  
  471.                         du_dy_left = compute_du_dy(t,vlt,vlb, recip_dy);
  472.                         dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dy);
  473.                         dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dy);
  474.  
  475.                         if (Lighting_enabled) {
  476.                                 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy);
  477.                                 lleft = v3d[vlt].l;
  478.                         }
  479.                 }
  480.  
  481.                 // See if we have reached the end of the current left edge, and if so, set
  482.                 // new values for dx_dy and x.  Not necessary to set new values for u,v.
  483.                 if (y == next_break_right) {
  484.                         fix     recip_dy;
  485.  
  486.                         while (y == f2i(v3d[vrb].y2d)) {
  487.                                 vrt = vrb;
  488.                                 vrb = succmod(vrb,t.nv);
  489.                         }
  490.  
  491.                         next_break_right = f2i(v3d[vrb].y2d);
  492.  
  493.                         dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
  494.                         recip_dy = fix_recip(dy);
  495.  
  496.                         dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy);
  497.  
  498.                         xright = v3d[vrt].x2d;
  499.                         zright = v3d[vrt].z;
  500.                         uright = fixmul(v3d[vrt].u,zright);
  501.                         vright = fixmul(v3d[vrt].v,zright);
  502.  
  503.                         du_dy_right = compute_du_dy(t,vrt,vrb, recip_dy);
  504.                         dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dy);
  505.                         dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dy);
  506.  
  507.                         if (Lighting_enabled) {
  508.                                 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy);
  509.                                 lright = v3d[vrt].l;
  510.                         }
  511.                 }
  512.  
  513.                 if (Lighting_enabled) {
  514.                         if (y >= Window_clip_top)
  515.                                 ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
  516.                         lleft += dl_dy_left;
  517.                         lright += dl_dy_right;
  518.                 } else
  519.                         if (y >= Window_clip_top)
  520.                                 ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
  521.  
  522.                 uleft += du_dy_left;
  523.                 vleft += dv_dy_left;
  524.  
  525.                 uright += du_dy_right;
  526.                 vright += dv_dy_right;
  527.  
  528.                 xleft += dx_dy_left;
  529.                 xright += dx_dy_right;
  530.  
  531.                 zleft += dz_dy_left;
  532.                 zright += dz_dy_right;
  533.  
  534.         }
  535.  
  536.         // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values,
  537.         //      but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta.
  538.  
  539.         ntmap_scanline_lighted(srcb,boty,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright);
  540. }
  541.  
  542.  
  543. // -------------------------------------------------------------------------------------
  544. //      Texture map current scanline using linear interpolation.
  545. // -------------------------------------------------------------------------------------
  546. static void ntmap_scanline_lighted_linear(const grs_bitmap &srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix lleft, fix lright)
  547. {
  548.         fix     dx,recip_dx,du_dx,dv_dx,dl_dx;
  549.  
  550.         dx = f2i(xright) - f2i(xleft);
  551.         if ((dx < 0) || (xright < 0) || (xleft > xright))               // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers
  552.                 return;
  553.  
  554.                 // setup to call assembler scanline renderer
  555.         recip_dx = fix_recip(dx);
  556.  
  557.                 du_dx = fixmul(uright - uleft,recip_dx);
  558.                 dv_dx = fixmul(vright - vleft,recip_dx);
  559.  
  560.                 fx_u = uleft;
  561.                 fx_v = vleft;
  562.                 fx_du_dx = du_dx;
  563.                 fx_dv_dx = dv_dx;
  564.                 fx_y = y;
  565.                 fx_xright = f2i(xright);
  566.                 fx_xleft = f2i(xleft);
  567.                 pixptr = srcb.bm_data;
  568.  
  569.                 switch (Lighting_enabled) {
  570.                         case 0:
  571.                                 //added 07/11/99 adb - prevent writing before the buffer
  572.                                 if (fx_xleft < 0)
  573.                                         fx_xleft = 0;
  574.                                 //end addition -adb
  575.                                
  576.                                 cur_tmap_scanline_lin_nolight();
  577.                                 break;
  578.                         case 1:
  579.                                 if (lleft < F1_0/2)
  580.                                         lleft = F1_0/2;
  581.                                 if (lright < F1_0/2)
  582.                                         lright = F1_0/2;
  583.  
  584.                                 if (lleft > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS)
  585.                                         lleft = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS;
  586.                                 if (lright > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS)
  587.                                         lright = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS;
  588.  
  589.                                 //added 07/11/99 adb - prevent writing before the buffer
  590.                                 if (fx_xleft < 0)
  591.                                         fx_xleft = 0;
  592.                                 //end addition -adb
  593.  
  594. {
  595.                         fix mul_thing;
  596.  
  597.                         fx_l = lleft;
  598.                         fx_dl_dx = fixmul(lright - lleft,recip_dx);
  599.  
  600.                         //      This is a pretty ugly hack to prevent lighting overflows.
  601.                         mul_thing = dx * fx_dl_dx;
  602.                         if (lleft + mul_thing < 0)
  603.                                 fx_dl_dx += 12;
  604.                         else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2))
  605.                                 fx_dl_dx -= 12;
  606. }
  607.  
  608.                                 fx_l = lleft;
  609.                                 dl_dx = fixmul(lright - lleft,recip_dx);
  610.                                 fx_dl_dx = dl_dx;
  611.                                 cur_tmap_scanline_lin();
  612.                                 break;
  613.                         case 2:
  614. #ifdef EDITOR_TMAP
  615.                                 fx_xright = f2i(xright);
  616.                                 fx_xleft = f2i(xleft);
  617.                                 tmap_flat_color = 1;
  618.                                 cur_tmap_scanline_flat();
  619. #else
  620.                                 Int3(); //      Illegal, called an editor only routine!
  621. #endif
  622.                                 break;
  623.                 }
  624. }
  625.  
  626. // -------------------------------------------------------------------------------------
  627. //      Render a texture map with lighting using perspective interpolation in inner and outer loops.
  628. // -------------------------------------------------------------------------------------
  629. static void ntexture_map_lighted_linear(const grs_bitmap &srcb, const g3ds_tmap &t)
  630. {
  631.         int     vlt,vrt,vlb,vrb;        // vertex left top, vertex right top, vertex left bottom, vertex right bottom
  632.         int     topy,boty,dy;
  633.         fix     dx_dy_left,dx_dy_right;
  634.         fix     du_dy_left,du_dy_right;
  635.         fix     dv_dy_left,dv_dy_right;
  636.         fix     dl_dy_left,dl_dy_right;
  637.         int     max_y_vertex;
  638.         fix     xleft,xright,uleft,vleft,uright,vright,lleft,lright;
  639.         int     next_break_left, next_break_right;
  640.         fix     recip_dyl, recip_dyr;
  641.  
  642.         //remove stupid warnings in compile
  643.         dl_dy_left = F1_0;
  644.         dl_dy_right = F1_0;
  645.         lleft = F1_0;
  646.         lright = F1_0;
  647.  
  648.         auto &v3d = t.verts;
  649.  
  650.         // Determine top and bottom y coords.
  651.         compute_y_bounds(t,vlt,vlb,vrt,vrb,max_y_vertex);
  652.  
  653.         // Set top and bottom (of entire texture map) y coordinates.
  654.         topy = f2i(v3d[vlt].y2d);
  655.         boty = f2i(v3d[max_y_vertex].y2d);
  656.  
  657.         if (topy > Window_clip_bot)
  658.                 return;
  659.         if (boty > Window_clip_bot)
  660.                 boty = Window_clip_bot;
  661.  
  662.         dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
  663.         recip_dyl = fix_recip(dy);
  664.  
  665.         dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
  666.         recip_dyr = fix_recip(dy);
  667.  
  668.         // Set amount to change x coordinate for each advance to next scanline.
  669.         dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl);
  670.         dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr);
  671.  
  672.         du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dyl);
  673.         du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dyr);
  674.  
  675.         dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dyl);
  676.         dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dyr);
  677.  
  678.         if (Lighting_enabled) {
  679.                 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl);
  680.                 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr);
  681.  
  682.                 lleft = v3d[vlt].l;
  683.                 lright = v3d[vrt].l;
  684.         }
  685.  
  686.         // Set initial values for x, u, v
  687.         xleft = v3d[vlt].x2d;
  688.         xright = v3d[vrt].x2d;
  689.  
  690.         uleft = v3d[vlt].u;
  691.         uright = v3d[vrt].u;
  692.         vleft = v3d[vlt].v;
  693.         vright = v3d[vrt].v;
  694.  
  695.         // scan all rows in texture map from top through first break.
  696.         next_break_left = f2i(v3d[vlb].y2d);
  697.         next_break_right = f2i(v3d[vrb].y2d);
  698.  
  699.         for (int y = topy; y < boty; y++) {
  700.  
  701.                 // See if we have reached the end of the current left edge, and if so, set
  702.                 // new values for dx_dy and x,u,v
  703.                 if (y == next_break_left) {
  704.                         fix     recip_dy;
  705.  
  706.                         // Handle problem of double points.  Search until y coord is different.  Cannot get
  707.                         // hung in an infinite loop because we know there is a vertex with a lower y coordinate
  708.                         // because in the for loop, we don't scan all spanlines.
  709.                         while (y == f2i(v3d[vlb].y2d)) {
  710.                                 vlt = vlb;
  711.                                 vlb = prevmod(vlb,t.nv);
  712.                         }
  713.                         next_break_left = f2i(v3d[vlb].y2d);
  714.  
  715.                         dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d);
  716.                         recip_dy = fix_recip(dy);
  717.  
  718.                         dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy);
  719.  
  720.                         xleft = v3d[vlt].x2d;
  721.                         uleft = v3d[vlt].u;
  722.                         vleft = v3d[vlt].v;
  723.                         lleft = v3d[vlt].l;
  724.  
  725.                         du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dy);
  726.                         dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dy);
  727.  
  728.                         if (Lighting_enabled) {
  729.                                 dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy);
  730.                                 lleft = v3d[vlt].l;
  731.                         }
  732.                 }
  733.  
  734.                 // See if we have reached the end of the current left edge, and if so, set
  735.                 // new values for dx_dy and x.  Not necessary to set new values for u,v.
  736.                 if (y == next_break_right) {
  737.                         fix     recip_dy;
  738.  
  739.                         while (y == f2i(v3d[vrb].y2d)) {
  740.                                 vrt = vrb;
  741.                                 vrb = succmod(vrb,t.nv);
  742.                         }
  743.  
  744.                         dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d);
  745.                         recip_dy = fix_recip(dy);
  746.  
  747.                         next_break_right = f2i(v3d[vrb].y2d);
  748.                         dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy);
  749.  
  750.                         xright = v3d[vrt].x2d;
  751.                         uright = v3d[vrt].u;
  752.                         vright = v3d[vrt].v;
  753.  
  754.                         du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dy);
  755.                         dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dy);
  756.  
  757.                         if (Lighting_enabled) {
  758.                                 dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy);
  759.                                 lright = v3d[vrt].l;
  760.                         }
  761.                 }
  762.  
  763.                 if (Lighting_enabled) {
  764.                         ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
  765.                         lleft += dl_dy_left;
  766.                         lright += dl_dy_right;
  767.                 } else
  768.                         ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
  769.  
  770.                 uleft += du_dy_left;
  771.                 vleft += dv_dy_left;
  772.  
  773.                 uright += du_dy_right;
  774.                 vright += dv_dy_right;
  775.  
  776.                 xleft += dx_dy_left;
  777.                 xright += dx_dy_right;
  778.  
  779.         }
  780.  
  781.         // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values,
  782.         //      but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta.
  783.  
  784.         ntmap_scanline_lighted_linear(srcb,boty,xleft,xright,uleft,uright,vleft,vright,lleft,lright);
  785. }
  786.  
  787. // fix  DivNum = F1_0*12;
  788.  
  789. // -------------------------------------------------------------------------------------
  790. // Interface from Matt's data structures to Mike's texture mapper.
  791. // -------------------------------------------------------------------------------------
  792. void draw_tmap(grs_canvas &canvas, const grs_bitmap &rbp, uint_fast32_t nverts, const g3s_point *const *vertbuf)
  793. {
  794.         //      These variables are used in system which renders texture maps which lie on one scanline as a line.
  795.         // fix  div_numerator;
  796.         int     lighting_on_save = Lighting_on;
  797.  
  798.         Assert(nverts <= MAX_TMAP_VERTS);
  799.  
  800.         const grs_bitmap *bp = &rbp;
  801.         //      If no transparency and seg depth is large, render as flat shaded.
  802.         if ((Current_seg_depth > Max_linear_depth) && ((bp->get_flag_mask(3)) == 0)) {
  803.                 draw_tmap_flat(canvas, rbp, nverts, vertbuf);
  804.                 return;
  805.         }
  806.  
  807.         bp = rle_expand_texture(*bp);           // Expand if rle'd
  808.  
  809.         Transparency_on = bp->get_flag_mask(BM_FLAG_TRANSPARENT);
  810.         if (bp->get_flag_mask(BM_FLAG_NO_LIGHTING))
  811.                 Lighting_on = 0;
  812.  
  813.  
  814.         // Setup texture map in Tmap1
  815.         g3ds_tmap Tmap1;
  816.         Tmap1.nv = nverts;                                              // Initialize number of vertices
  817.  
  818. //      div_numerator = DivNum; //f1_0*3;
  819.  
  820.         for (int i=0; i<nverts; i++) {
  821.                 g3ds_vertex     *tvp = &Tmap1.verts[i];
  822.                 auto vp = vertbuf[i];
  823.  
  824.                 tvp->x2d = vp->p3_sx;
  825.                 tvp->y2d = vp->p3_sy;
  826.  
  827.                 //      Check for overflow on fixdiv.  Will overflow on vp->z <= something small.  Allow only as low as 256.
  828.                 auto clipped_p3_z = std::max(256, vp->p3_z);
  829.                 tvp->z = fixdiv(F1_0*12, clipped_p3_z);
  830.                 tvp->u = vp->p3_u << 6; //* bp->bm_w;
  831.                 tvp->v = vp->p3_v << 6; //* bp->bm_h;
  832.  
  833.                 Assert(Lighting_on < 3);
  834.  
  835.                 if (Lighting_on)
  836.                         tvp->l = vp->p3_l * NUM_LIGHTING_LEVELS;
  837.         }
  838.  
  839.  
  840.         Lighting_enabled = Lighting_on;
  841.  
  842.         // Now, call my texture mapper.
  843.                 switch (Interpolation_method) { // 0 = choose, 1 = linear, 2 = /8 perspective, 3 = full perspective
  844.                         case 0:                                                         // choose best interpolation
  845.                                 if (Current_seg_depth > Max_perspective_depth)
  846.                                 {
  847.                                 case 1:                                                         // linear interpolation
  848.                                         ntexture_map_lighted_linear(*bp, Tmap1);
  849.                                 }
  850.                                 else
  851.                                 {
  852.                                         DXX_BOOST_FALLTHROUGH;
  853.                                 case 2:                                                         // perspective every 8th pixel interpolation
  854.                                 case 3:                                                         // perspective every pixel interpolation
  855.                                         ntexture_map_lighted(*bp, Tmap1);
  856.                                 }
  857.                                 break;
  858.                         default:
  859.                                 Assert(0);                              // Illegal value for Interpolation_method, must be 0,1,2,3
  860.                 }
  861.  
  862.         Lighting_on = lighting_on_save;
  863.  
  864. }
  865.  
  866. }
  867.