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.  * Lighting functions.
  23.  *
  24.  */
  25.  
  26. #include <algorithm>
  27. #include <bitset>
  28. #include <numeric>
  29. #include <stdio.h>
  30. #include <string.h>     // for memset()
  31.  
  32. #include "render_state.h"
  33. #include "maths.h"
  34. #include "vecmat.h"
  35. #include "gr.h"
  36. #include "inferno.h"
  37. #include "segment.h"
  38. #include "dxxerror.h"
  39. #include "render.h"
  40. #include "game.h"
  41. #include "vclip.h"
  42. #include "lighting.h"
  43. #include "3d.h"
  44. #include "interp.h"
  45. #include "gameseg.h"
  46. #include "laser.h"
  47. #include "timer.h"
  48. #include "player.h"
  49. #include "playsave.h"
  50. #include "weapon.h"
  51. #include "powerup.h"
  52. #include "fvi.h"
  53. #include "object.h"
  54. #include "robot.h"
  55. #include "multi.h"
  56. #include "palette.h"
  57. #include "bm.h"
  58. #include "rle.h"
  59. #include "wall.h"
  60.  
  61. #include "compiler-range_for.h"
  62. #include "partial_range.h"
  63. #include "d_range.h"
  64.  
  65. using std::min;
  66.  
  67. static int Do_dynamic_light=1;
  68. static int use_fcd_lighting;
  69.  
  70. #define HEADLIGHT_CONE_DOT      (F1_0*9/10)
  71. #define HEADLIGHT_SCALE         (F1_0*10)
  72.  
  73. namespace dcx {
  74.  
  75. static void add_light_div(g3s_lrgb &d, const g3s_lrgb &light, const fix &scale)
  76. {
  77.         d.r += fixdiv(light.r, scale);
  78.         d.g += fixdiv(light.g, scale);
  79.         d.b += fixdiv(light.b, scale);
  80. }
  81.  
  82. static void add_light_dot_square(g3s_lrgb &d, const g3s_lrgb &light, const fix &dot)
  83. {
  84.         auto square = fixmul(dot, dot);
  85.         d.r += fixmul(square, light.r)/8;
  86.         d.g += fixmul(square, light.g)/8;
  87.         d.b += fixmul(square, light.b)/8;
  88. }
  89.  
  90. static fix compute_player_light_emission_intensity(const object_base &objp)
  91. {
  92.         auto &phys_info = objp.mtype.phys_info;
  93.         const auto drag = phys_info.drag;
  94.         const fix k = fixmuldiv(phys_info.mass, drag, (F1_0 - drag));
  95.         // smooth thrust value like set_thrust_from_velocity()
  96.         const auto sthrust = vm_vec_copy_scale(phys_info.velocity, k);
  97.         return std::max(static_cast<fix>(vm_vec_mag_quick(sthrust) / 4), F2_0) + F0_5;
  98. }
  99.  
  100. static fix compute_fireball_light_emission_intensity(const d_vclip_array &Vclip, const object_base &objp)
  101. {
  102.         const auto oid = get_fireball_id(objp);
  103.         if (oid >= Vclip.size())
  104.                 return 0;
  105.         auto &v = Vclip[oid];
  106.         const auto light_intensity = v.light_value;
  107.         if (objp.lifeleft < F1_0*4)
  108.                 return fixmul(fixdiv(objp.lifeleft, v.play_time), light_intensity);
  109.         return light_intensity;
  110. }
  111.  
  112. }
  113.  
  114. // ----------------------------------------------------------------------------------------------
  115. namespace dsx {
  116.  
  117. static void apply_light(fvmsegptridx &vmsegptridx, const g3s_lrgb obj_light_emission, const vcsegptridx_t obj_seg, const vms_vector &obj_pos, const unsigned n_render_vertices, std::array<unsigned, MAX_VERTICES> &render_vertices, const std::array<segnum_t, MAX_VERTICES> &vert_segnum_list, const icobjptridx_t objnum)
  118. {
  119.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  120.         auto &Vertices = LevelSharedVertexState.get_vertices();
  121.         if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0)
  122.         {
  123.                 fix obji_64 = ((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3)*64;
  124.                 sbyte is_marker = 0;
  125. #if defined(DXX_BUILD_DESCENT_II)
  126.                 if (objnum && objnum->type == OBJ_MARKER)
  127.                                 is_marker = 1;
  128. #endif
  129.  
  130.                 auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
  131.                 auto &vcvertptr = Vertices.vcptr;
  132.                 // for pretty dim sources, only process vertices in object's own segment.
  133.                 //      12/04/95, MK, markers only cast light in own segment.
  134.                 if ((abs(obji_64) <= F1_0*8) || is_marker) {
  135.                         auto &vp = obj_seg->verts;
  136.  
  137.                         range_for (const auto vertnum, vp)
  138.                         {
  139.                                 fix                     dist;
  140.                                 auto &vertpos = *vcvertptr(vertnum);
  141.                                 dist = vm_vec_dist_quick(obj_pos, vertpos);
  142.                                 dist = fixmul(dist/4, dist/4);
  143.                                 if (dist < abs(obji_64)) {
  144.                                         if (dist < MIN_LIGHT_DIST)
  145.                                                 dist = MIN_LIGHT_DIST;
  146.  
  147.                                         add_light_div(Dynamic_light[vertnum], obj_light_emission, dist);
  148.                                 }
  149.                         }
  150.                 } else {
  151.                         int     headlight_shift = 0;
  152.                         fix     max_headlight_dist = F1_0*200;
  153.  
  154. #if defined(DXX_BUILD_DESCENT_II)
  155.                         if (objnum)
  156.                         {
  157.                                 const object &obj = *objnum;
  158.                                 if (obj.type == OBJ_PLAYER)
  159.                                         if (obj.ctype.player_info.powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON) {
  160.                                                 headlight_shift = 3;
  161.                                                 if (get_player_id(obj) != Player_num)
  162.                                                 {
  163.                                                         fvi_query       fq;
  164.                                                         fvi_info                hit_data;
  165.                                                         int                     fate;
  166.  
  167.                                                         const auto tvec = vm_vec_scale_add(obj.pos, obj.orient.fvec, F1_0*200);
  168.  
  169.                                                         fq.startseg                             = obj_seg;
  170.                                                         fq.p0                                           = &obj.pos;
  171.                                                         fq.p1                                           = &tvec;
  172.                                                         fq.rad                                  = 0;
  173.                                                         fq.thisobjnum                   = objnum;
  174.                                                         fq.ignore_obj_list.first = nullptr;
  175.                                                         fq.flags                                        = FQ_TRANSWALL;
  176.  
  177.                                                         fate = find_vector_intersection(fq, hit_data);
  178.                                                         if (fate != HIT_NONE)
  179.                                                                 max_headlight_dist = vm_vec_mag_quick(vm_vec_sub(hit_data.hit_pnt, obj.pos)) + F1_0*4;
  180.                                                 }
  181.                                         }
  182.                         }
  183. #endif
  184.                         range_for (const unsigned vv, xrange(n_render_vertices))
  185.                         {
  186.                                 fix                     dist;
  187.                                 int                     apply_light = 0;
  188.  
  189.                                 const auto vertnum = render_vertices[vv];
  190.                                 auto vsegnum = vert_segnum_list[vv];
  191.                                 auto &vertpos = *vcvertptr(vertnum);
  192.  
  193.                                 if (use_fcd_lighting && abs(obji_64) > F1_0*32)
  194.                                 {
  195.                                         dist = find_connected_distance(obj_pos, obj_seg, vertpos, vmsegptridx(vsegnum), n_render_vertices, WID_RENDPAST_FLAG|WID_FLY_FLAG);
  196.                                         if (dist >= 0)
  197.                                                 apply_light = 1;
  198.                                 }
  199.                                 else
  200.                                 {
  201.                                         dist = vm_vec_dist_quick(obj_pos, vertpos);
  202.                                         apply_light = 1;
  203.                                 }
  204.  
  205.                                 if (apply_light && ((dist >> headlight_shift) < abs(obji_64))) {
  206.  
  207.                                         if (dist < MIN_LIGHT_DIST)
  208.                                                 dist = MIN_LIGHT_DIST;
  209.  
  210.                                         if (headlight_shift && objnum)
  211.                                         {
  212.                                                 fix dot;
  213.                                                 // MK, Optimization note: You compute distance about 15 lines up, this is partially redundant
  214.                                                 const auto vec_to_point = vm_vec_normalized_quick(vm_vec_sub(vertpos, obj_pos));
  215.                                                 dot = vm_vec_dot(vec_to_point, objnum->orient.fvec);
  216.                                                 if (dot < F1_0/2)
  217.                                                 {
  218.                                                         // Do the normal thing, but darken around headlight.
  219.                                                         add_light_div(Dynamic_light[vertnum], obj_light_emission, fixmul(HEADLIGHT_SCALE, dist));
  220.                                                 }
  221.                                                 else
  222.                                                 {
  223.                                                         if (!(Game_mode & GM_MULTI) || dist < max_headlight_dist)
  224.                                                         {
  225.                                                                 add_light_dot_square(Dynamic_light[vertnum], obj_light_emission, dot);
  226.                                                         }
  227.                                                 }
  228.                                         }
  229.                                         else
  230.                                         {
  231.                                                 add_light_div(Dynamic_light[vertnum], obj_light_emission, dist);
  232.                                         }
  233.                                 }
  234.                         }
  235.                 }
  236.         }
  237. }
  238. }
  239.  
  240. #define FLASH_LEN_FIXED_SECONDS (F1_0/3)
  241. #define FLASH_SCALE             (3*F1_0/FLASH_LEN_FIXED_SECONDS)
  242.  
  243. // ----------------------------------------------------------------------------------------------
  244. static void cast_muzzle_flash_light(fvmsegptridx &vmsegptridx, int n_render_vertices, std::array<unsigned, MAX_VERTICES> &render_vertices, const std::array<segnum_t, MAX_VERTICES> &vert_segnum_list)
  245. {
  246.         fix64 current_time;
  247.         short time_since_flash;
  248.  
  249.         current_time = timer_query();
  250.  
  251.         range_for (auto &i, Muzzle_data)
  252.         {
  253.                 if (i.create_time)
  254.                 {
  255.                         time_since_flash = current_time - i.create_time;
  256.                         if (time_since_flash < FLASH_LEN_FIXED_SECONDS)
  257.                         {
  258.                                 g3s_lrgb ml;
  259.                                 ml.r = ml.g = ml.b = ((FLASH_LEN_FIXED_SECONDS - time_since_flash) * FLASH_SCALE);
  260.                                 apply_light(vmsegptridx, ml, vmsegptridx(i.segnum), i.pos, n_render_vertices, render_vertices, vert_segnum_list, object_none);
  261.                         }
  262.                         else
  263.                         {
  264.                                 i.create_time = 0; // turn off this muzzle flash
  265.                         }
  266.                 }
  267.         }
  268. }
  269.  
  270. // Translation table to make flares flicker at different rates
  271. const std::array<fix, 16> Obj_light_xlate{{0x1234, 0x3321, 0x2468, 0x1735,
  272.                             0x0123, 0x19af, 0x3f03, 0x232a,
  273.                             0x2123, 0x39af, 0x0f03, 0x132a,
  274.                             0x3123, 0x29af, 0x1f03, 0x032a
  275. }};
  276. #if defined(DXX_BUILD_DESCENT_I)
  277. #define compute_player_light_emission_intensity(LevelUniqueHeadlightState, obj) compute_player_light_emission_intensity(obj)
  278. #define compute_light_emission(LevelSharedRobotInfoState, LevelUniqueHeadlightState, Vclip, obj)        compute_light_emission(Vclip, obj)
  279. #elif defined(DXX_BUILD_DESCENT_II)
  280. #undef compute_player_light_emission_intensity
  281. #undef compute_light_emission
  282. #endif
  283.  
  284. // ---------------------------------------------------------
  285. namespace dsx {
  286.  
  287. #if defined(DXX_BUILD_DESCENT_II)
  288. static fix compute_player_light_emission_intensity(d_level_unique_headlight_state &LevelUniqueHeadlightState, const object &objp)
  289. {
  290.         if (objp.ctype.player_info.powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON)
  291.         {
  292.                 auto &Headlights = LevelUniqueHeadlightState.Headlights;
  293.                 auto &Num_headlights = LevelUniqueHeadlightState.Num_headlights;
  294.                 if (Num_headlights < Headlights.size())
  295.                         Headlights[Num_headlights++] = &objp;
  296.                 return HEADLIGHT_SCALE;
  297.         }
  298.         uint8_t hoard_orbs;
  299.         // If hoard game and player, add extra light based on how many orbs you have Pulse as well.
  300.         if (game_mode_hoard() && (hoard_orbs = objp.ctype.player_info.hoard.orbs))
  301.         {
  302.                 const fix hoardlight = 1 + (i2f(hoard_orbs) / 2);
  303.                 const auto s = fix_sin(static_cast<fix>(GameTime64 >> 1) & 0xFFFF); // probably a bad way to do it
  304.                 return fixmul((s + F1_0) >> 1, hoardlight);
  305.         }
  306.         return compute_player_light_emission_intensity(objp);
  307. }
  308. #endif
  309.  
  310. static g3s_lrgb compute_light_emission(const d_level_shared_robot_info_state &LevelSharedRobotInfoState, d_level_unique_headlight_state &LevelUniqueHeadlightState, const d_vclip_array &Vclip, const vcobjptridx_t obj)
  311. {
  312.         int compute_color = 0;
  313.         fix light_intensity = 0;
  314. #if defined(DXX_BUILD_DESCENT_II)
  315.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  316. #endif
  317.         const object &objp = obj;
  318.         switch (objp.type)
  319.         {
  320.                 case OBJ_PLAYER:
  321.                         light_intensity = compute_player_light_emission_intensity(LevelUniqueHeadlightState, objp);
  322.                         break;
  323.                 case OBJ_FIREBALL:
  324.                         light_intensity = compute_fireball_light_emission_intensity(Vclip, objp);
  325.                         break;
  326.                 case OBJ_ROBOT:
  327. #if defined(DXX_BUILD_DESCENT_I)
  328.                         light_intensity = F1_0/2;       // F1_0*Robot_info[obj->id].lightcast;
  329. #elif defined(DXX_BUILD_DESCENT_II)
  330.                         light_intensity = F1_0*Robot_info[get_robot_id(objp)].lightcast;
  331. #endif
  332.                         break;
  333.                 case OBJ_WEAPON:
  334.                 {
  335.                         const auto wid = get_weapon_id(objp);
  336.                         const fix tval = Weapon_info[wid].light;
  337.                         if (wid == weapon_id_type::FLARE_ID)
  338.                                 light_intensity = 2 * (min(tval, objp.lifeleft) + ((static_cast<fix>(GameTime64) ^ Obj_light_xlate[obj.get_unchecked_index() % Obj_light_xlate.size()]) & 0x3fff));
  339.                         else
  340.                                 light_intensity = tval;
  341.                         break;
  342.                 }
  343. #if defined(DXX_BUILD_DESCENT_II)
  344.                 case OBJ_MARKER:
  345.                 {
  346.                         fix lightval = objp.lifeleft;
  347.  
  348.                         lightval &= 0xffff;
  349.                         lightval = 8 * abs(F1_0/2 - lightval);
  350.  
  351.                         light_intensity = lightval;
  352.                         break;
  353.                 }
  354. #endif
  355.                 case OBJ_POWERUP:
  356.                         light_intensity = Powerup_info[get_powerup_id(objp)].light;
  357.                         break;
  358.                 case OBJ_DEBRIS:
  359.                         light_intensity = F1_0/4;
  360.                         break;
  361.                 case OBJ_LIGHT:
  362.                         light_intensity = objp.ctype.light_info.intensity;
  363.                         break;
  364.                 default:
  365.                         light_intensity = 0;
  366.                         break;
  367.         }
  368.  
  369.         const auto &&white_light = [light_intensity] {
  370.                 return g3s_lrgb{light_intensity, light_intensity, light_intensity};
  371.         };
  372.  
  373.         if (!PlayerCfg.DynLightColor) // colored lights not desired so use intensity only OR no intensity (== no light == no color) at all
  374.                 return white_light();
  375.  
  376.         switch (objp.type) // find out if given object should cast colored light and compute if so
  377.         {
  378.                 default:
  379.                         break;
  380.                 case OBJ_FIREBALL:
  381.                 case OBJ_WEAPON:
  382. #if defined(DXX_BUILD_DESCENT_II)
  383.                 case OBJ_MARKER:
  384. #endif
  385.                         compute_color = 1;
  386.                         break;
  387.                 case OBJ_POWERUP:
  388.                 {
  389.                         switch (get_powerup_id(objp))
  390.                         {
  391.                                 case POW_EXTRA_LIFE:
  392.                                 case POW_ENERGY:
  393.                                 case POW_SHIELD_BOOST:
  394.                                 case POW_KEY_BLUE:
  395.                                 case POW_KEY_RED:
  396.                                 case POW_KEY_GOLD:
  397.                                 case POW_CLOAK:
  398.                                 case POW_INVULNERABILITY:
  399. #if defined(DXX_BUILD_DESCENT_II)
  400.                                 case POW_HOARD_ORB:
  401. #endif
  402.                                         compute_color = 1;
  403.                                         break;
  404.                                 default:
  405.                                         break;
  406.                         }
  407.                         break;
  408.                 }
  409.         }
  410.  
  411.         if (compute_color)
  412.         {
  413.                 int t_idx_s = -1, t_idx_e = -1;
  414.  
  415.                 if (light_intensity < F1_0) // for every effect we want color, increase light_intensity so the effect becomes barely visible
  416.                         light_intensity = F1_0;
  417.  
  418.                 g3s_lrgb obj_color = { 255, 255, 255 };
  419.  
  420.                 switch (objp.render_type)
  421.                 {
  422.                         case RT_NONE:
  423.                                 break; // no object - no light
  424.                         case RT_POLYOBJ:
  425.                         {
  426.                                 auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
  427.                                 const polymodel *const po = &Polygon_models[objp.rtype.pobj_info.model_num];
  428.                                 if (po->n_textures <= 0)
  429.                                 {
  430.                                         int color = g3_poly_get_color(po->model_data.get());
  431.                                         if (color)
  432.                                         {
  433.                                                 obj_color.r = gr_current_pal[color].r;
  434.                                                 obj_color.g = gr_current_pal[color].g;
  435.                                                 obj_color.b = gr_current_pal[color].b;
  436.                                         }
  437.                                 }
  438.                                 else
  439.                                 {
  440.                                         t_idx_s = ObjBitmaps[ObjBitmapPtrs[po->first_texture]].index;
  441.                                         t_idx_e = t_idx_s + po->n_textures - 1;
  442.                                 }
  443.                                 break;
  444.                         }
  445.                         case RT_LASER:
  446.                         {
  447.                                 t_idx_s = t_idx_e = Weapon_info[get_weapon_id(objp)].bitmap.index;
  448.                                 break;
  449.                         }
  450.                         case RT_POWERUP:
  451.                         {
  452.                                 auto &v = Vclip[objp.rtype.vclip_info.vclip_num];
  453.                                 auto &f = v.frames;
  454.                                 t_idx_s = f[0].index;
  455.                                 t_idx_e = f[v.num_frames - 1].index;
  456.                                 break;
  457.                         }
  458.                         case RT_WEAPON_VCLIP:
  459.                         {
  460.                                 auto &v = Vclip[Weapon_info[get_weapon_id(objp)].weapon_vclip];
  461.                                 auto &f = v.frames;
  462.                                 t_idx_s = f[0].index;
  463.                                 t_idx_e = f[v.num_frames - 1].index;
  464.                                 break;
  465.                         }
  466.                         default:
  467.                         {
  468.                                 const auto &vc = Vclip[objp.id];
  469.                                 t_idx_s = vc.frames[0].index;
  470.                                 t_idx_e = vc.frames[vc.num_frames-1].index;
  471.                                 break;
  472.                         }
  473.                 }
  474.  
  475.                 if (t_idx_s != -1 && t_idx_e != -1)
  476.                 {
  477.                         obj_color.r = obj_color.g = obj_color.b = 0;
  478.                         range_for (const int i, xrange(t_idx_s, t_idx_e + 1))
  479.                         {
  480.                                 grs_bitmap *bm = &GameBitmaps[i];
  481.                                 bitmap_index bi;
  482.                                 bi.index = i;
  483.                                 PIGGY_PAGE_IN(bi);
  484.                                 obj_color.r += bm->avg_color_rgb[0];
  485.                                 obj_color.g += bm->avg_color_rgb[1];
  486.                                 obj_color.b += bm->avg_color_rgb[2];
  487.                         }
  488.                 }
  489.  
  490.                 const fix rgbsum = obj_color.r + obj_color.g + obj_color.b;
  491.                 // obviously this object did not give us any usable color. so let's do our own but with blackjack and hookers!
  492.                 if (rgbsum <= 0)
  493.                         return white_light();
  494.                 // scale color to light intensity
  495.                 const float cscale = static_cast<float>(light_intensity * 3) / rgbsum;
  496.                 return g3s_lrgb{
  497.                         static_cast<fix>(obj_color.r * cscale),
  498.                         static_cast<fix>(obj_color.g * cscale),
  499.                         static_cast<fix>(obj_color.b * cscale)
  500.                 };
  501.         }
  502.  
  503.         return white_light();
  504. }
  505.  
  506. // ----------------------------------------------------------------------------------------------
  507. void set_dynamic_light(render_state_t &rstate)
  508. {
  509.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  510.         auto &Objects = LevelUniqueObjectState.Objects;
  511.         auto &Vertices = LevelSharedVertexState.get_vertices();
  512.         auto &vcobjptridx = Objects.vcptridx;
  513.         std::array<unsigned, MAX_VERTICES> render_vertices;
  514.         std::array<segnum_t, MAX_VERTICES> vert_segnum_list;
  515.         static fix light_time;
  516.  
  517. #if defined(DXX_BUILD_DESCENT_II)
  518.         LevelUniqueLightState.Num_headlights = 0;
  519. #endif
  520.  
  521.         if (!Do_dynamic_light)
  522.                 return;
  523.  
  524.         light_time += FrameTime;
  525.         if (light_time < (F1_0/60)) // it's enough to stress the CPU 60 times per second
  526.                 return;
  527.         light_time = light_time - (F1_0/60);
  528.  
  529.         std::bitset<MAX_VERTICES> render_vertex_flags;
  530.  
  531.         //      Create list of vertices that need to be looked at for setting of ambient light.
  532.         auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
  533.         uint_fast32_t n_render_vertices = 0;
  534.         range_for (const auto segnum, partial_const_range(rstate.Render_list, rstate.N_render_segs))
  535.         {
  536.                 if (segnum != segment_none) {
  537.                         auto &vp = Segments[segnum].verts;
  538.                         range_for (const auto vnum, vp)
  539.                         {
  540.                                 if (vnum > Vertices.get_count() - 1)
  541.                                 {
  542.                                         Int3();         //invalid vertex number
  543.                                         continue;       //ignore it, and go on to next one
  544.                                 }
  545.                                 auto &&b = render_vertex_flags[vnum];
  546.                                 if (!b)
  547.                                 {
  548.                                         b = true;
  549.                                         render_vertices[n_render_vertices] = vnum;
  550.                                         vert_segnum_list[n_render_vertices] = segnum;
  551.                                         n_render_vertices++;
  552.                                         Dynamic_light[vnum] = {};
  553.                                 }
  554.                         }
  555.                 }
  556.         }
  557.  
  558.         cast_muzzle_flash_light(vmsegptridx, n_render_vertices, render_vertices, vert_segnum_list);
  559.  
  560.         range_for (const auto &&obj, vcobjptridx)
  561.         {
  562.                 const object &objp = obj;
  563.                 if (objp.type == OBJ_NONE)
  564.                         continue;
  565.                 const auto &&obj_light_emission = compute_light_emission(LevelSharedRobotInfoState, LevelUniqueLightState, Vclip, obj);
  566.  
  567.                 if (((obj_light_emission.r+obj_light_emission.g+obj_light_emission.b)/3) > 0)
  568.                         apply_light(vmsegptridx, obj_light_emission, vcsegptridx(objp.segnum), objp.pos, n_render_vertices, render_vertices, vert_segnum_list, obj);
  569.         }
  570. }
  571.  
  572. // ---------------------------------------------------------
  573.  
  574. #if defined(DXX_BUILD_DESCENT_II)
  575.  
  576. void toggle_headlight_active(object &player)
  577. {
  578.         auto &player_info = player.ctype.player_info;
  579.         if (player_info.powerup_flags & PLAYER_FLAGS_HEADLIGHT) {
  580.                 player_info.powerup_flags ^= PLAYER_FLAGS_HEADLIGHT_ON;
  581.                 if (Game_mode & GM_MULTI)
  582.                         multi_send_flags(player.id);
  583.         }
  584. }
  585.  
  586. static fix compute_headlight_light_on_object(const d_level_unique_headlight_state &LevelUniqueHeadlightState, const object_base &objp)
  587. {
  588.         fix     light;
  589.  
  590.         //      Let's just illuminate players and robots for speed reasons, ok?
  591.         if (objp.type != OBJ_ROBOT && objp.type != OBJ_PLAYER)
  592.                 return 0;
  593.  
  594.         light = 0;
  595.  
  596.         range_for (const object_base *const light_objp, partial_const_range(LevelUniqueHeadlightState.Headlights, LevelUniqueHeadlightState.Num_headlights))
  597.         {
  598.                 auto vec_to_obj = vm_vec_sub(objp.pos, light_objp->pos);
  599.                 const fix dist = vm_vec_normalize_quick(vec_to_obj);
  600.                 if (dist > 0) {
  601.                         const fix dot = vm_vec_dot(light_objp->orient.fvec, vec_to_obj);
  602.  
  603.                         if (dot < F1_0/2)
  604.                                 light += fixdiv(HEADLIGHT_SCALE, fixmul(HEADLIGHT_SCALE, dist));        //      Do the normal thing, but darken around headlight.
  605.                         else
  606.                                 light += fixmul(fixmul(dot, dot), HEADLIGHT_SCALE)/8;
  607.                 }
  608.         }
  609.         return light;
  610. }
  611. #endif
  612.  
  613. }
  614.  
  615. //compute the average dynamic light in a segment.  Takes the segment number
  616. static g3s_lrgb compute_seg_dynamic_light(const std::array<g3s_lrgb, MAX_VERTICES> &Dynamic_light, const shared_segment &seg)
  617. {
  618.         const auto &&op = [&Dynamic_light](g3s_lrgb r, const unsigned v) {
  619.                 r.r += Dynamic_light[v].r;
  620.                 r.g += Dynamic_light[v].g;
  621.                 r.b += Dynamic_light[v].b;
  622.                 return r;
  623.         };
  624.         g3s_lrgb sum = std::accumulate(begin(seg.verts), end(seg.verts), g3s_lrgb{0, 0, 0}, op);
  625.         sum.r >>= 3;
  626.         sum.g >>= 3;
  627.         sum.b >>= 3;
  628.         return sum;
  629. }
  630.  
  631. static std::array<g3s_lrgb, MAX_OBJECTS> object_light;
  632. static std::array<object_signature_t, MAX_OBJECTS> object_sig;
  633. const object *old_viewer;
  634. static int reset_lighting_hack;
  635. #define LIGHT_RATE i2f(4) //how fast the light ramps up
  636.  
  637. void start_lighting_frame(const object &viewer)
  638. {
  639.         reset_lighting_hack = (&viewer != old_viewer);
  640.         old_viewer = &viewer;
  641. }
  642.  
  643. namespace dsx {
  644.  
  645. //compute the lighting for an object.  Takes a pointer to the object,
  646. //and possibly a rotated 3d point.  If the point isn't specified, the
  647. //object's center point is rotated.
  648. g3s_lrgb compute_object_light(const d_level_unique_light_state &LevelUniqueLightState, const vcobjptridx_t obj)
  649. {
  650.         g3s_lrgb light;
  651.         const vcobjidx_t objnum = obj;
  652.  
  653.         //First, get static (mono) light for this segment
  654.         const auto &&objsegp = vcsegptr(obj->segnum);
  655.         light.r = light.g = light.b = objsegp->static_light;
  656.  
  657.         auto &os = object_sig[objnum];
  658.         auto &ol = object_light[objnum];
  659.         //Now, maybe return different value to smooth transitions
  660.         if (!reset_lighting_hack && os == obj->signature)
  661.         {
  662.                 fix frame_delta;
  663.                 g3s_lrgb delta_light;
  664.  
  665.                 delta_light.r = light.r - ol.r;
  666.                 delta_light.g = light.g - ol.g;
  667.                 delta_light.b = light.b - ol.b;
  668.  
  669.                 frame_delta = fixmul(LIGHT_RATE,FrameTime);
  670.  
  671.                 if (abs(((delta_light.r+delta_light.g+delta_light.b)/3)) <= frame_delta)
  672.                 {
  673.                         ol = light;             //we've hit the goal
  674.                 }
  675.                 else
  676.                 {
  677.                         if (((delta_light.r+delta_light.g+delta_light.b)/3) < 0)
  678.                                 frame_delta = -frame_delta;
  679.                         ol.r += frame_delta;
  680.                         ol.g += frame_delta;
  681.                         ol.b += frame_delta;
  682.                         light = ol;
  683.                 }
  684.  
  685.         }
  686.         else //new object, initialize
  687.         {
  688.                 os = obj->signature;
  689.                 ol = light;
  690.         }
  691.  
  692.         //Finally, add in dynamic light for this segment
  693.         auto &Dynamic_light = LevelUniqueLightState.Dynamic_light;
  694.         const auto &&seg_dl = compute_seg_dynamic_light(Dynamic_light, objsegp);
  695. #if defined(DXX_BUILD_DESCENT_II)
  696.         //Next, add in (NOTE: WHITE) headlight on this object
  697.         const fix mlight = compute_headlight_light_on_object(LevelUniqueLightState, obj);
  698.         light.r += mlight;
  699.         light.g += mlight;
  700.         light.b += mlight;
  701. #endif
  702.  
  703.         light.r += seg_dl.r;
  704.         light.g += seg_dl.g;
  705.         light.b += seg_dl.b;
  706.  
  707.         return light;
  708. }
  709.  
  710. }
  711.