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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Code to render cool external-scene terrain
  23.  *
  24.  */
  25.  
  26. #include <bitset>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30.  
  31. #include "3d.h"
  32. #include "dxxerror.h"
  33. #include "gr.h"
  34. #include "texmap.h"
  35. #include "iff.h"
  36. #include "u_mem.h"
  37. #include "inferno.h"
  38. #include "textures.h"
  39. #include "object.h"
  40. #include "endlevel.h"
  41. #include "fireball.h"
  42. #include "render.h"
  43. #include "player.h"
  44. #include "segment.h"
  45. #include "terrain.h"
  46. #include <memory>
  47.  
  48. #define GRID_MAX_SIZE   64
  49. #define GRID_SCALE      i2f(2*20)
  50. #define HEIGHT_SCALE    f1_0
  51.  
  52. static int grid_w,grid_h;
  53.  
  54. static RAIIdmem<ubyte[]> height_array;
  55. static std::unique_ptr<uint8_t[]> light_array;
  56.  
  57. #define HEIGHT(_i,_j) (height_array[(_i)*grid_w+(_j)])
  58. #define LIGHT(_i,_j) light_array[(_i)*grid_w+(_j)]
  59.  
  60. //!!#define HEIGHT(_i,_j)   height_array[(grid_h-1-j)*grid_w+(_i)]
  61. //!!#define LIGHT(_i,_j)    light_array[(grid_h-1-j)*grid_w+(_i)]
  62.  
  63. #define LIGHTVAL(_i,_j) (static_cast<fix>(LIGHT(_i, _j)) << 8)
  64.  
  65. static grs_bitmap *terrain_bm;
  66. static int terrain_outline=0;
  67. static int org_i,org_j;
  68.  
  69. // LINT: adding function prototypes
  70. static void build_light_table(void);
  71.  
  72. // ------------------------------------------------------------------------
  73. static void draw_cell(grs_canvas &canvas, const vms_vector &Viewer_eye, const int i, const int j, cg3s_point &p0, cg3s_point &p1, cg3s_point &p2, cg3s_point &p3, int &mine_tiles_drawn)
  74. {
  75.         std::array<cg3s_point *, 3> pointlist;
  76.  
  77.         pointlist[0] = &p0;
  78.         pointlist[1] = &p1;
  79.         pointlist[2] = &p3;
  80.         std::array<g3s_lrgb, 3> lrgb_list1;
  81.         std::array<g3s_uvl, 3> uvl_list1;
  82.         lrgb_list1[0].r = lrgb_list1[0].g = lrgb_list1[0].b = uvl_list1[0].l = LIGHTVAL(i,j);
  83.         lrgb_list1[1].r = lrgb_list1[1].g = lrgb_list1[1].b = uvl_list1[1].l = LIGHTVAL(i,j+1);
  84.         lrgb_list1[2].r = lrgb_list1[2].g = lrgb_list1[2].b = uvl_list1[2].l = LIGHTVAL(i+1,j);
  85.  
  86.         uvl_list1[0].u = (i)*f1_0/4; uvl_list1[0].v = (j)*f1_0/4;
  87.         uvl_list1[1].u = (i)*f1_0/4; uvl_list1[1].v = (j+1)*f1_0/4;
  88.         uvl_list1[2].u = (i+1)*f1_0/4;   uvl_list1[2].v = (j)*f1_0/4;
  89.  
  90.         g3_check_and_draw_tmap(canvas, pointlist, uvl_list1, lrgb_list1, *terrain_bm);
  91.         if (terrain_outline) {
  92. #if !DXX_USE_OGL
  93.                 const int lsave = Lighting_on;
  94.                 Lighting_on=0;
  95. #endif
  96.                 const uint8_t color = BM_XRGB(31, 0, 0);
  97.                 g3_draw_line(canvas, *pointlist[0],*pointlist[1], color);
  98.                 g3_draw_line(canvas, *pointlist[2],*pointlist[0], color);
  99. #if !DXX_USE_OGL
  100.                 Lighting_on=lsave;
  101. #endif
  102.         }
  103.  
  104.         pointlist[0] = &p1;
  105.         pointlist[1] = &p2;
  106.         std::array<g3s_uvl, 3> uvl_list2;
  107.         std::array<g3s_lrgb, 3> lrgb_list2;
  108.         lrgb_list2[0].r = lrgb_list2[0].g = lrgb_list2[0].b = uvl_list2[0].l = LIGHTVAL(i,j+1);
  109.         lrgb_list2[1].r = lrgb_list2[1].g = lrgb_list2[1].b = uvl_list2[1].l = LIGHTVAL(i+1,j+1);
  110.         lrgb_list2[2].r = lrgb_list2[2].g = lrgb_list2[2].b = uvl_list2[2].l = LIGHTVAL(i+1,j);
  111.  
  112.         uvl_list2[0].u = (i)*f1_0/4; uvl_list2[0].v = (j+1)*f1_0/4;
  113.         uvl_list2[1].u = (i+1)*f1_0/4;   uvl_list2[1].v = (j+1)*f1_0/4;
  114.         uvl_list2[2].u = (i+1)*f1_0/4;   uvl_list2[2].v = (j)*f1_0/4;
  115.  
  116.         g3_check_and_draw_tmap(canvas, pointlist, uvl_list2, lrgb_list2, *terrain_bm);
  117.         if (terrain_outline) {
  118. #if !DXX_USE_OGL
  119.                 const int lsave = Lighting_on;
  120.                 Lighting_on=0;
  121. #endif
  122.                 const uint8_t color = BM_XRGB(31, 0, 0);
  123.                 g3_draw_line(canvas, *pointlist[0],*pointlist[1], color);
  124.                 g3_draw_line(canvas, *pointlist[1],*pointlist[2], color);
  125.                 g3_draw_line(canvas, *pointlist[2],*pointlist[0], color);
  126. #if !DXX_USE_OGL
  127.                 Lighting_on=lsave;
  128. #endif
  129.         }
  130.  
  131.         if (i==org_i && j==org_j)
  132.                 mine_tiles_drawn |= 1;
  133.         if (i==org_i-1 && j==org_j)
  134.                 mine_tiles_drawn |= 2;
  135.         if (i==org_i && j==org_j-1)
  136.                 mine_tiles_drawn |= 4;
  137.         if (i==org_i-1 && j==org_j-1)
  138.                 mine_tiles_drawn |= 8;
  139.  
  140.         if (mine_tiles_drawn == 0xf) {
  141.                 //draw_exit_model();
  142.                 mine_tiles_drawn=-1;
  143.                 window_rendered_data window;
  144.                 render_mine(canvas, Viewer_eye, PlayerUniqueEndlevelState.exit_segnum, 0, window);
  145.                 //if (ext_expl_playing)
  146.                 //      draw_fireball(&external_explosion);
  147.         }
  148.  
  149. }
  150.  
  151. namespace {
  152.  
  153. class terrain_y_cache
  154. {
  155.         static const std::size_t cache_size = 256;
  156.         std::bitset<cache_size> yc_flags;
  157.         std::array<vms_vector, cache_size> y_cache;
  158. public:
  159.         vms_vector &operator()(uint_fast32_t h);
  160. };
  161.  
  162. }
  163.  
  164. vms_vector &terrain_y_cache::operator()(uint_fast32_t h)
  165. {
  166.         auto &dyp = y_cache[h];
  167.         if (auto &&ycf = yc_flags[h])
  168.         {
  169.         }
  170.         else
  171.         {
  172.                 ycf = 1;
  173.                 const auto tv = vm_vec_copy_scale(surface_orient.uvec,h*HEIGHT_SCALE);
  174.                 g3_rotate_delta_vec(dyp,tv);
  175.         }
  176.         return dyp;
  177. }
  178.  
  179. void render_terrain(grs_canvas &canvas, const vms_vector &Viewer_eye, const vms_vector &org_point,int org_2dx,int org_2dy)
  180. {
  181.         vms_vector delta_i,delta_j;             //delta_y;
  182.         g3s_point p,save_p_low,save_p_high;
  183.         g3s_point last_p2;
  184.         int i,j;
  185.         int low_i,high_i,low_j,high_j;
  186.         int viewer_i,viewer_j;
  187.         org_i = org_2dy;
  188.         org_j = org_2dx;
  189.  
  190.         low_i = 0;  high_i = grid_w-1;
  191.         low_j = 0;  high_j = grid_h-1;
  192.  
  193.         //@@start_point.x = org_point->x - GRID_SCALE*(org_i - low_i);
  194.         //@@start_point.z = org_point->z - GRID_SCALE*(org_j - low_j);
  195.         //@@start_point.y = org_point->y;
  196.         terrain_y_cache get_dy_vec;
  197.  
  198. #if !DXX_USE_OGL
  199.         Interpolation_method = 1;
  200. #endif
  201.  
  202.         {
  203.         const auto tv = vm_vec_copy_scale(surface_orient.rvec,GRID_SCALE);
  204.         g3_rotate_delta_vec(delta_i,tv);
  205.         }
  206.         {
  207.         const auto tv = vm_vec_copy_scale(surface_orient.fvec,GRID_SCALE);
  208.         g3_rotate_delta_vec(delta_j,tv);
  209.         }
  210.  
  211.         auto start_point = vm_vec_scale_add(org_point,surface_orient.rvec,-(org_i - low_i)*GRID_SCALE);
  212.         vm_vec_scale_add2(start_point,surface_orient.fvec,-(org_j - low_j)*GRID_SCALE);
  213.  
  214.         {
  215.                 const auto tv = vm_vec_sub(Viewer->pos,start_point);
  216.         viewer_i = vm_vec_dot(tv,surface_orient.rvec) / GRID_SCALE;
  217.         viewer_j = vm_vec_dot(tv,surface_orient.fvec) / GRID_SCALE;
  218.         }
  219.  
  220.         auto last_p = g3_rotate_point(start_point);
  221.         save_p_low = last_p;
  222.  
  223.         g3s_point save_row[GRID_MAX_SIZE]{};
  224.         // Is this needed?
  225.         for (j=low_j;j<=high_j;j++) {
  226.                 g3_add_delta_vec(save_row[j],last_p,get_dy_vec(HEIGHT(low_i,j)));
  227.                 if (j==high_j)
  228.                         save_p_high = last_p;
  229.                 else
  230.                         g3_add_delta_vec(last_p,last_p,delta_j);
  231.         }
  232.  
  233.         int mine_tiles_drawn = 0;    //flags to tell if all 4 tiles under mine have drawn
  234.         for (i=low_i;i<viewer_i;i++) {
  235.  
  236.                 g3_add_delta_vec(save_p_low,save_p_low,delta_i);
  237.                 last_p = save_p_low;
  238.                 g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i+1,low_j)));
  239.                
  240.                 for (j=low_j;j<viewer_j;j++) {
  241.                         g3s_point p2;
  242.  
  243.                         g3_add_delta_vec(p,last_p,delta_j);
  244.                         g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i+1,j+1)));
  245.  
  246.                         draw_cell(canvas, Viewer_eye, i, j, save_row[j], save_row[j+1], p2, last_p2, mine_tiles_drawn);
  247.  
  248.                         last_p = p;
  249.                         save_row[j] = last_p2;
  250.                         last_p2 = p2;
  251.  
  252.                 }
  253.  
  254.                 vm_vec_negate(delta_j);                 //don't have a delta sub...
  255.  
  256.                 g3_add_delta_vec(save_p_high,save_p_high,delta_i);
  257.                 last_p = save_p_high;
  258.                 g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i+1,high_j)));
  259.                
  260.                 for (j=high_j-1;j>=viewer_j;j--) {
  261.                         g3s_point p2;
  262.  
  263.                         g3_add_delta_vec(p,last_p,delta_j);
  264.                         g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i+1,j)));
  265.  
  266.                         draw_cell(canvas, Viewer_eye, i, j, save_row[j], save_row[j+1], last_p2, p2, mine_tiles_drawn);
  267.  
  268.                         last_p = p;
  269.                         save_row[j+1] = last_p2;
  270.                         last_p2 = p2;
  271.  
  272.                 }
  273.  
  274.                 save_row[j+1] = last_p2;
  275.  
  276.                 vm_vec_negate(delta_j);         //restore sign of j
  277.  
  278.         }
  279.  
  280.         //now do i from other end
  281.  
  282.         vm_vec_negate(delta_i);         //going the other way now...
  283.  
  284.         //@@start_point.x += (high_i-low_i)*GRID_SCALE;
  285.         vm_vec_scale_add2(start_point,surface_orient.rvec,(high_i-low_i)*GRID_SCALE);
  286.         g3_rotate_point(last_p,start_point);
  287.         save_p_low = last_p;
  288.  
  289.         for (j=low_j;j<=high_j;j++) {
  290.                 g3_add_delta_vec(save_row[j],last_p,get_dy_vec(HEIGHT(high_i,j)));
  291.                 if (j==high_j)
  292.                         save_p_high = last_p;
  293.                 else
  294.                         g3_add_delta_vec(last_p,last_p,delta_j);
  295.         }
  296.  
  297.         for (i=high_i-1;i>=viewer_i;i--) {
  298.  
  299.                 g3_add_delta_vec(save_p_low,save_p_low,delta_i);
  300.                 last_p = save_p_low;
  301.                 g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i,low_j)));
  302.                
  303.                 for (j=low_j;j<viewer_j;j++) {
  304.                         g3s_point p2;
  305.  
  306.                         g3_add_delta_vec(p,last_p,delta_j);
  307.                         g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i,j+1)));
  308.  
  309.                         draw_cell(canvas, Viewer_eye, i, j, last_p2, p2, save_row[j+1], save_row[j], mine_tiles_drawn);
  310.  
  311.                         last_p = p;
  312.                         save_row[j] = last_p2;
  313.                         last_p2 = p2;
  314.  
  315.                 }
  316.  
  317.                 vm_vec_negate(delta_j);                 //don't have a delta sub...
  318.  
  319.                 g3_add_delta_vec(save_p_high,save_p_high,delta_i);
  320.                 last_p = save_p_high;
  321.                 g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i,high_j)));
  322.                
  323.                 for (j=high_j-1;j>=viewer_j;j--) {
  324.                         g3s_point p2;
  325.  
  326.                         g3_add_delta_vec(p,last_p,delta_j);
  327.                         g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i,j)));
  328.  
  329.                         draw_cell(canvas, Viewer_eye, i, j, p2, last_p2, save_row[j+1], save_row[j], mine_tiles_drawn);
  330.  
  331.                         last_p = p;
  332.                         save_row[j+1] = last_p2;
  333.                         last_p2 = p2;
  334.  
  335.                 }
  336.  
  337.                 save_row[j+1] = last_p2;
  338.  
  339.                 vm_vec_negate(delta_j);         //restore sign of j
  340.  
  341.         }
  342.  
  343. }
  344.  
  345. void free_height_array()
  346. {
  347.         height_array.reset();
  348. }
  349.  
  350. void load_terrain(const char *filename)
  351. {
  352.         grs_bitmap height_bitmap;
  353.         int iff_error;
  354.         int i,j;
  355.         ubyte h,min_h,max_h;
  356.  
  357.         iff_error = iff_read_bitmap(filename, height_bitmap, NULL);
  358.         if (iff_error != IFF_NO_ERROR) {
  359.                 Error("File %s - IFF error: %s",filename,iff_errormsg(iff_error));
  360.         }
  361.         grid_w = height_bitmap.bm_w;
  362.         grid_h = height_bitmap.bm_h;
  363.  
  364.         Assert(grid_w <= GRID_MAX_SIZE);
  365.         Assert(grid_h <= GRID_MAX_SIZE);
  366.  
  367.         height_array.reset(height_bitmap.get_bitmap_data());
  368.  
  369.         max_h=0; min_h=255;
  370.         for (i=0;i<grid_w;i++)
  371.                 for (j=0;j<grid_h;j++) {
  372.  
  373.                         h = HEIGHT(i,j);
  374.  
  375.                         if (h > max_h)
  376.                                 max_h = h;
  377.  
  378.                         if (h < min_h)
  379.                                 min_h = h;
  380.                 }
  381.  
  382.         for (i=0;i<grid_w;i++)
  383.                 for (j=0;j<grid_h;j++)
  384.                         HEIGHT(i,j) -= min_h;
  385.        
  386.  
  387. //      d_free(height_bitmap.bm_data);
  388.  
  389.         terrain_bm = terrain_bitmap;
  390.  
  391.         build_light_table();
  392. }
  393.  
  394.  
  395. static void get_pnt(vms_vector &p,int i,int j)
  396. {
  397.         // added on 02/20/99 by adb to prevent overflow
  398.         if (i >= grid_h) i = grid_h - 1;
  399.         if (i == grid_h - 1 && j >= grid_w) j = grid_w - 1;
  400.         // end additions by adb
  401.         p.x = GRID_SCALE*i;
  402.         p.z = GRID_SCALE*j;
  403.         p.y = HEIGHT(i,j)*HEIGHT_SCALE;
  404. }
  405.  
  406. constexpr vms_vector light{0x2e14,0xe8f5,0x5eb8};
  407.  
  408. static fix get_face_light(const vms_vector &p0,const vms_vector &p1,const vms_vector &p2)
  409. {
  410.         const auto norm = vm_vec_normal(p0,p1,p2);
  411.         return -vm_vec_dot(norm,light);
  412. }
  413.  
  414. static fix get_avg_light(int i,int j)
  415. {
  416.         vms_vector pp,p[6];
  417.         fix sum;
  418.         int f;
  419.  
  420.         get_pnt(pp,i,j);
  421.         get_pnt(p[0],i-1,j);
  422.         get_pnt(p[1],i,j-1);
  423.         get_pnt(p[2],i+1,j-1);
  424.         get_pnt(p[3],i+1,j);
  425.         get_pnt(p[4],i,j+1);
  426.         get_pnt(p[5],i-1,j+1);
  427.  
  428.         for (f=0,sum=0;f<6;f++)
  429.                 sum += get_face_light(pp,p[f],p[(f+1)%5]);
  430.  
  431.         return sum/6;
  432. }
  433.  
  434. void free_light_table()
  435. {
  436.         light_array.reset();
  437. }
  438.  
  439. static void build_light_table()
  440. {
  441.         std::size_t alloc = grid_w*grid_h;
  442.         light_array = std::make_unique<uint8_t[]>(alloc);
  443.         memset(light_array.get(), 0, alloc);
  444.         int i,j;
  445.         fix l, l2, min_l = INT32_MAX, max_l = 0;
  446.         for (i=1;i<grid_w;i++)
  447.                 for (j=1;j<grid_h;j++) {
  448.                         l = get_avg_light(i,j);
  449.  
  450.                         if (l > max_l)
  451.                                 max_l = l;
  452.  
  453.                         if (l < min_l)
  454.                                 min_l = l;
  455.                 }
  456.  
  457.         for (i=1;i<grid_w;i++)
  458.                 for (j=1;j<grid_h;j++) {
  459.  
  460.                         l = get_avg_light(i,j);
  461.  
  462.                         if (min_l == max_l) {
  463.                                 LIGHT(i,j) = l>>8;
  464.                                 continue;
  465.                         }
  466.  
  467.                         l2 = fixdiv((l-min_l),(max_l-min_l));
  468.  
  469.                         if (l2==f1_0)
  470.                                 l2--;
  471.  
  472.                         LIGHT(i,j) = l2>>8;
  473.  
  474.                 }
  475. }
  476.