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.  * u,v coordinate computation for segment faces
  23.  *
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <stdarg.h>
  29. #include <math.h>
  30. #include <string.h>
  31. #include "inferno.h"
  32. #include "segment.h"
  33. #include "editor/editor.h"
  34. #include "editor/esegment.h"
  35. #include "gameseg.h"
  36. #include "maths.h"
  37. #include "dxxerror.h"
  38. #include "wall.h"
  39. #include "editor/kdefs.h"
  40. #include "bm.h"         //      Needed for TmapInfo
  41. #include        "effects.h"     //      Needed for effects_bm_num
  42. #include "fvi.h"
  43. #include "seguvs.h"
  44.  
  45. #include "compiler-range_for.h"
  46. #include "d_enumerate.h"
  47. #include "d_range.h"
  48. #include "d_zip.h"
  49.  
  50. namespace dcx {
  51. static void cast_all_light_in_mine(int quick_flag);
  52. }
  53. //--rotate_uvs-- vms_vector Rightvec;
  54.  
  55. #define MAX_LIGHT_SEGS 16
  56.  
  57. //      ---------------------------------------------------------------------------------------------
  58. //      Scan all polys in all segments, return average light value for vnum.
  59. //      segs = output array for segments containing vertex, terminated by -1.
  60. static fix get_average_light_at_vertex(int vnum, segnum_t *segs)
  61. {
  62.         fix     total_light;
  63.         int     num_occurrences;
  64. //      #ifndef NDEBUG //Removed this ifdef because the version of Assert that I used to get it to compile doesn't work without this symbol. -KRB
  65.         segnum_t   *original_segs;
  66.  
  67.         original_segs = segs;
  68. //      #endif
  69.  
  70.  
  71.         num_occurrences = 0;
  72.         total_light = 0;
  73.  
  74.         range_for (const auto &&segp, vcsegptridx)
  75.         {
  76.                 auto e = end(segp->verts);
  77.                 auto relvnum = std::distance(std::find(begin(segp->verts), e, vnum), e);
  78.                 if (relvnum < MAX_VERTICES_PER_SEGMENT) {
  79.  
  80.                         *segs++ = segp;
  81.                         Assert(segs - original_segs < MAX_LIGHT_SEGS);
  82.                         (void)original_segs;
  83.  
  84.                         range_for (const auto &&z, zip(segp->children, segp->unique_segment::sides, Side_to_verts))
  85.                         {
  86.                                 if (!IS_CHILD(std::get<0>(z))) {
  87.                                         auto &uside = std::get<1>(z);
  88.                                         auto &vp = std::get<2>(z);
  89.                                         const auto vb = begin(vp);
  90.                                         const auto ve = end(vp);
  91.                                         const auto vi = std::find(vb, ve, relvnum);
  92.                                         if (vi != ve)
  93.                                         {
  94.                                                 const auto v = std::distance(vb, vi);
  95.                                                 total_light += uside.uvls[v].l;
  96.                                                         num_occurrences++;
  97.                                                 }
  98.                                 }       // end if
  99.                         }       // end sidenum
  100.                 }
  101.         }       // end segnum
  102.  
  103.         *segs = segment_none;
  104.  
  105.         if (num_occurrences)
  106.                 return total_light/num_occurrences;
  107.         else
  108.                 return 0;
  109.  
  110. }
  111.  
  112. static void set_average_light_at_vertex(int vnum)
  113. {
  114.         int     relvnum;
  115.         segnum_t        Segment_indices[MAX_LIGHT_SEGS];
  116.         int     segind;
  117.  
  118.         fix average_light;
  119.  
  120.         average_light = get_average_light_at_vertex(vnum, Segment_indices);
  121.  
  122.         if (!average_light)
  123.                 return;
  124.  
  125.         segind = 0;
  126.         while (Segment_indices[segind] != segment_none) {
  127.                 auto segnum = Segment_indices[segind++];
  128.  
  129.                 auto &ssegp = *vcsegptr(segnum);
  130.                 unique_segment &usegp = *vmsegptr(segnum);
  131.  
  132.                 for (relvnum=0; relvnum<MAX_VERTICES_PER_SEGMENT; relvnum++)
  133.                         if (ssegp.verts[relvnum] == vnum)
  134.                                 break;
  135.  
  136.                 if (relvnum < MAX_VERTICES_PER_SEGMENT) {
  137.                         range_for (const auto &&z, zip(ssegp.children, usegp.sides, Side_to_verts))
  138.                         {
  139.                                 if (!IS_CHILD(std::get<0>(z))) {
  140.                                         unique_side &sidep = std::get<1>(z);
  141.                                         auto &vp = std::get<2>(z);
  142.                                         const auto vb = begin(vp);
  143.                                         const auto ve = end(vp);
  144.                                         const auto vi = std::find(vb, ve, relvnum);
  145.                                         if (vi != ve)
  146.                                         {
  147.                                                 const auto v = std::distance(vb, vi);
  148.                                                 sidep.uvls[v].l = average_light;
  149.                                         }
  150.                                 }       // end if
  151.                         }       // end sidenum
  152.                 }       // end if
  153.         }       // end while
  154.  
  155.         Update_flags |= UF_WORLD_CHANGED;
  156. }
  157.  
  158. static void set_average_light_on_side(const vmsegptr_t segp, int sidenum)
  159. {
  160.         if (!IS_CHILD(segp->children[sidenum]))
  161.                 range_for (const auto v, Side_to_verts[sidenum])
  162.                 {
  163.                         set_average_light_at_vertex(segp->verts[v]);
  164.                 }
  165.  
  166. }
  167.  
  168. int set_average_light_on_curside(void)
  169. {
  170.         set_average_light_on_side(Cursegp, Curside);
  171.         return 0;
  172. }
  173.  
  174. int set_average_light_on_all(void)
  175. {
  176.         Doing_lighting_hack_flag = 1;
  177.         cast_all_light_in_mine(0);
  178.         Doing_lighting_hack_flag = 0;
  179.         Update_flags |= UF_WORLD_CHANGED;
  180.  
  181. //      int seg, side;
  182.  
  183. //      for (seg=0; seg<=Highest_segment_index; seg++)
  184. //              for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
  185. //                      if (Segments[seg].segnum != -1)
  186. //                              set_average_light_on_side(&Segments[seg], side);
  187.         return 0;
  188. }
  189.  
  190. int set_average_light_on_all_quick(void)
  191. {
  192.         cast_all_light_in_mine(1);
  193.         Update_flags |= UF_WORLD_CHANGED;
  194.  
  195.         return 0;
  196. }
  197.  
  198. //      ---------------------------------------------------------------------------------------------
  199. //      Given a polygon, compress the uv coordinates so that they are as close to 0 as possible.
  200. //      Do this by adding a constant u and v to each uv pair.
  201. static void compress_uv_coordinates(std::array<uvl, 4> &uvls)
  202. {
  203.         fix     uc, vc;
  204.  
  205.         uc = 0;
  206.         vc = 0;
  207.  
  208.         range_for (auto &uvl, uvls)
  209.         {
  210.                 uc += uvl.u;
  211.                 vc += uvl.v;
  212.         }
  213.  
  214.         uc /= 4;
  215.         vc /= 4;
  216.         uc = uc & 0xffff0000;
  217.         vc = vc & 0xffff0000;
  218.  
  219.         range_for (auto &uvl, uvls)
  220.         {
  221.                 uvl.u -= uc;
  222.                 uvl.v -= vc;
  223.         }
  224. }
  225.  
  226. static void assign_default_lighting_on_side(std::array<uvl, 4> &uvls)
  227. {
  228.         range_for (auto &uvl, uvls)
  229.                 uvl.l = DEFAULT_LIGHTING;
  230. }
  231.  
  232. static void assign_default_lighting(unique_segment &segp)
  233. {
  234.         range_for (auto &side, segp.sides)
  235.                 assign_default_lighting_on_side(side.uvls);
  236. }
  237.  
  238. void assign_default_lighting_all(void)
  239. {
  240.         range_for (const auto &&segp, vmsegptr)
  241.         {
  242.                 if (segp->segnum != segment_none)
  243.                         assign_default_lighting(segp);
  244.         }
  245. }
  246.  
  247. //      ---------------------------------------------------------------------------------------------
  248. static void validate_uv_coordinates(unique_segment &segp)
  249. {
  250.         range_for (auto &side, segp.sides)
  251.         {
  252.                 compress_uv_coordinates(side.uvls);
  253.         }
  254.  
  255. }
  256.  
  257. #ifdef __WATCOMC__
  258. fix zhypot(fix a,fix b);
  259. #pragma aux zhypot parm [eax] [ebx] value [eax] modify [eax ebx ecx edx] = \
  260.         "imul   eax" \
  261.         "xchg eax,ebx" \
  262.         "mov    ecx,edx" \
  263.         "imul eax" \
  264.         "add    eax,ebx" \
  265.         "adc    edx,ecx" \
  266.         "call   quad_sqrt";
  267. #else
  268. static fix zhypot(fix a,fix b) {
  269.         double x = static_cast<double>(a) / 65536;
  270.         double y = static_cast<double>(b) / 65536;
  271.         return static_cast<long>(sqrt(x * x + y * y) * 65536);
  272. }
  273. #endif
  274.  
  275. //      ---------------------------------------------------------------------------------------------
  276. //      Assign lighting value to side, a function of the normal vector.
  277. void assign_light_to_side(unique_side &s)
  278. {
  279.         range_for (auto &v, s.uvls)
  280.                 v.l = DEFAULT_LIGHTING;
  281. }
  282.  
  283. fix     Stretch_scale_x = F1_0;
  284. fix     Stretch_scale_y = F1_0;
  285.  
  286. //      ---------------------------------------------------------------------------------------------
  287. //      Given u,v coordinates at two vertices, assign u,v coordinates to other two vertices on a side.
  288. //      (Actually, assign them to the coordinates in the faces.)
  289. //      va, vb = face-relative vertex indices corresponding to uva, uvb.  Ie, they are always in 0..3 and should be looked up in
  290. //      Side_to_verts[side] to get the segment relative index.
  291. static void assign_uvs_to_side(fvcvertptr &vcvertptr, const vmsegptridx_t segp, int sidenum, uvl *uva, uvl *uvb, int va, int vb)
  292. {
  293.         int                     vlo,vhi;
  294.         unsigned v0, v1, v2, v3;
  295.         std::array<uvl, 4> uvls;
  296.         uvl ruvmag,fuvmag,uvlo,uvhi;
  297.         fix                     fmag,mag01;
  298.         Assert( (va<4) && (vb<4) );
  299.         Assert((abs(va - vb) == 1) || (abs(va - vb) == 3));             // make sure the verticies specify an edge
  300.  
  301.         auto &vp = Side_to_verts[sidenum];
  302.  
  303.         // We want vlo precedes vhi, ie vlo < vhi, or vlo = 3, vhi = 0
  304.         if (va == ((vb + 1) % 4)) {             // va = vb + 1
  305.                 vlo = vb;
  306.                 vhi = va;
  307.                 uvlo = *uvb;
  308.                 uvhi = *uva;
  309.         } else {
  310.                 vlo = va;
  311.                 vhi = vb;
  312.                 uvlo = *uva;
  313.                 uvhi = *uvb;
  314.         }
  315.  
  316.         Assert(((vlo+1) % 4) == vhi);   // If we are on an edge, then uvhi is one more than uvlo (mod 4)
  317.         uvls[vlo] = uvlo;
  318.         uvls[vhi] = uvhi;
  319.  
  320.         // Now we have vlo precedes vhi, compute vertices ((vhi+1) % 4) and ((vhi+2) % 4)
  321.  
  322.         // Assign u,v scale to a unit length right vector.
  323.         fmag = zhypot(uvhi.v - uvlo.v,uvhi.u - uvlo.u);
  324.         if (fmag < 64) {                // this is a fix, so 64 = 1/1024
  325.                 ruvmag.u = F1_0*256;
  326.                 ruvmag.v = F1_0*256;
  327.                 fuvmag.u = F1_0*256;
  328.                 fuvmag.v = F1_0*256;
  329.         } else {
  330.                 ruvmag.u = uvhi.v - uvlo.v;
  331.                 ruvmag.v = uvlo.u - uvhi.u;
  332.  
  333.                 fuvmag.u = uvhi.u - uvlo.u;
  334.                 fuvmag.v = uvhi.v - uvlo.v;
  335.         }
  336.  
  337.         v0 = segp->verts[vp[vlo]];
  338.         v1 = segp->verts[vp[vhi]];
  339.         v2 = segp->verts[vp[(vhi+1)%4]];
  340.         v3 = segp->verts[vp[(vhi+2)%4]];
  341.  
  342.         //      Compute right vector by computing orientation matrix from:
  343.         //              forward vector = vlo:vhi
  344.         //                right vector = vlo:(vhi+2) % 4
  345.         const auto &&vp0 = vcvertptr(v0);
  346.         const auto &vv1v0 = vm_vec_sub(vcvertptr(v1), vp0);
  347.         mag01 = vm_vec_mag(vv1v0);
  348.         mag01 = fixmul(mag01, (va == 0 || va == 2) ? Stretch_scale_x : Stretch_scale_y);
  349.  
  350.         if (unlikely(mag01 < F1_0/1024))
  351.                 editor_status_fmt("U, V bogosity in segment #%hu, probably on side #%i.  CLEAN UP YOUR MESS!", static_cast<uint16_t>(segp), sidenum);
  352.         else {
  353.                 struct frvec {
  354.                         vms_vector fvec, rvec;
  355.                         frvec(const vms_vector &tfvec, const vms_vector &trvec) {
  356.                                 if ((tfvec.x == 0 && tfvec.y == 0 && tfvec.z == 0) ||
  357.                                         (trvec.x == 0 && trvec.y == 0 && trvec.z == 0))
  358.                                 {
  359.                                         fvec = vmd_identity_matrix.fvec;
  360.                                         rvec = vmd_identity_matrix.rvec;
  361.                                 }
  362.                                 else
  363.                                 {
  364.                                         const auto &m = vm_vector_2_matrix(tfvec, nullptr, &trvec);
  365.                                         fvec = m.fvec;
  366.                                         rvec = m.rvec;
  367.                                 }
  368.                                 vm_vec_negate(rvec);
  369.                         }
  370.                 };
  371.                 const auto &vv3v0 = vm_vec_sub(vcvertptr(v3), vp0);
  372.                 const frvec fr{
  373.                         vv1v0,
  374.                         vv3v0
  375.                 };
  376.                 const auto assign_uvl = [&](const vms_vector &tvec, const uvl &uvi) {
  377.                         const auto drt = vm_vec_dot(fr.rvec, tvec);
  378.                         const auto dft = vm_vec_dot(fr.fvec, tvec);
  379.                         return uvl{
  380.                                 uvi.u +
  381.                                         fixdiv(fixmul(ruvmag.u, drt), mag01) +
  382.                                         fixdiv(fixmul(fuvmag.u, dft), mag01),
  383.                                 uvi.v +
  384.                                         fixdiv(fixmul(ruvmag.v, drt), mag01) +
  385.                                         fixdiv(fixmul(fuvmag.v, dft), mag01),
  386.                                 uvi.l
  387.                         };
  388.                 };
  389.                 uvls[(vhi+1)%4] = assign_uvl(vm_vec_sub(vcvertptr(v2), vcvertptr(v1)), uvhi);
  390.                 uvls[(vhi+2)%4] = assign_uvl(vv3v0, uvlo);
  391.                 //      For all faces in side, copy uv coordinates from uvs array to face.
  392.                 segp->unique_segment::sides[sidenum].uvls = uvls;
  393.         }
  394. }
  395.  
  396.  
  397. int Vmag = VMAG;
  398.  
  399. namespace dsx {
  400.  
  401. // -----------------------------------------------------------------------------------------------------------
  402. //      Assign default uvs to side.
  403. //      This means:
  404. //              v0 = 0,0
  405. //              v1 = k,0 where k is 3d size dependent
  406. //      v2, v3 assigned by assign_uvs_to_side
  407. void assign_default_uvs_to_side(const vmsegptridx_t segp, const unsigned side)
  408. {
  409.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  410.         auto &Vertices = LevelSharedVertexState.get_vertices();
  411.         uvl                     uv0,uv1;
  412.         uv0.u = 0;
  413.         uv0.v = 0;
  414.         auto &vp = Side_to_verts[side];
  415.         uv1.u = 0;
  416.         auto &vcvertptr = Vertices.vcptr;
  417.         uv1.v = Num_tilings * fixmul(Vmag, vm_vec_dist(vcvertptr(segp->verts[vp[1]]), vcvertptr(segp->verts[vp[0]])));
  418.  
  419.         assign_uvs_to_side(vcvertptr, segp, side, &uv0, &uv1, 0, 1);
  420. }
  421.  
  422. // -----------------------------------------------------------------------------------------------------------
  423. //      Assign default uvs to side.
  424. //      This means:
  425. //              v0 = 0,0
  426. //              v1 = k,0 where k is 3d size dependent
  427. //      v2, v3 assigned by assign_uvs_to_side
  428. void stretch_uvs_from_curedge(const vmsegptridx_t segp, int side)
  429. {
  430.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  431.         auto &Vertices = LevelSharedVertexState.get_vertices();
  432.         uvl                     uv0,uv1;
  433.         int                     v0, v1;
  434.  
  435.         v0 = Curedge;
  436.         v1 = (v0 + 1) % 4;
  437.  
  438.         auto &uvls = segp->unique_segment::sides[side].uvls;
  439.         uv0.u = uvls[v0].u;
  440.         uv0.v = uvls[v0].v;
  441.  
  442.         uv1.u = uvls[v1].u;
  443.         uv1.v = uvls[v1].v;
  444.  
  445.         auto &vcvertptr = Vertices.vcptr;
  446.         assign_uvs_to_side(vcvertptr, segp, side, &uv0, &uv1, v0, v1);
  447. }
  448.  
  449. // --------------------------------------------------------------------------------------------------------------
  450. //      Assign default uvs to a segment.
  451. void assign_default_uvs_to_segment(const vmsegptridx_t segp)
  452. {
  453.         range_for (const uint_fast32_t s, xrange(MAX_SIDES_PER_SEGMENT))
  454.         {
  455.                 assign_default_uvs_to_side(segp,s);
  456.                 assign_light_to_side(segp, s);
  457.         }
  458. }
  459.  
  460.  
  461. // -- mk021394 -- // --------------------------------------------------------------------------------------------------------------
  462. // -- mk021394 -- //    Find the face:poly:vertex index in base_seg:base_common_side which is segment relative vertex v1
  463. // -- mk021394 -- //    This very specific routine is subsidiary to med_assign_uvs_to_side.
  464. // -- mk021394 -- void get_face_and_vert(segment *base_seg, int base_common_side, int v1, int *ff, int *vv, int *pi)
  465. // -- mk021394 -- {
  466. // -- mk021394 --       int     p,f,v;
  467. // -- mk021394 --
  468. // -- mk021394 --       for (f=0; f<base_seg->sides[base_common_side].num_faces; f++) {
  469. // -- mk021394 --               face *fp = &base_seg->sides[base_common_side].faces[f];
  470. // -- mk021394 --               for (p=0; p<fp->num_polys; p++) {
  471. // -- mk021394 --                       poly *pp = &fp->polys[p];
  472. // -- mk021394 --                       for (v=0; v<pp->num_vertices; v++)
  473. // -- mk021394 --                               if (pp->verts[v] == v1) {
  474. // -- mk021394 --                                       *ff = f;
  475. // -- mk021394 --                                       *vv = v;
  476. // -- mk021394 --                                       *pi = p;
  477. // -- mk021394 --                                       return;
  478. // -- mk021394 --                               }
  479. // -- mk021394 --               }
  480. // -- mk021394 --       }
  481. // -- mk021394 --
  482. // -- mk021394 --       Assert(0);      // Error -- Couldn't find face:vertex which matched vertex v1 on base_seg:base_common_side
  483. // -- mk021394 -- }
  484.  
  485. // -- mk021394 -- // --------------------------------------------------------------------------------------------------------------
  486. // -- mk021394 -- //    Find the vertex index in base_seg:base_common_side which is segment relative vertex v1
  487. // -- mk021394 -- //    This very specific routine is subsidiary to med_assign_uvs_to_side.
  488. // -- mk021394 -- void get_side_vert(segment *base_seg,int base_common_side,int v1,int *vv)
  489. // -- mk021394 -- {
  490. // -- mk021394 --       int     p,f,v;
  491. // -- mk021394 --
  492. // -- mk021394 --       Assert((base_seg->sides[base_common_side].tri_edge == 0) || (base_seg->sides[base_common_side].tri_edge == 1));
  493. // -- mk021394 --       Assert(base_seg->sides[base_common_side].num_faces <= 2);
  494. // -- mk021394 --
  495. // -- mk021394 --       for (f=0; f<base_seg->sides[base_common_side].num_faces; f++) {
  496. // -- mk021394 --               face *fp = &base_seg->sides[base_common_side].faces[f];
  497. // -- mk021394 --               for (p=0; p<fp->num_polys; p++) {
  498. // -- mk021394 --                       poly    *pp = &fp->polys[p];
  499. // -- mk021394 --                       for (v=0; v<pp->num_vertices; v++)
  500. // -- mk021394 --                               if (pp->verts[v] == v1) {
  501. // -- mk021394 --                                       if (pp->num_vertices == 4) {
  502. // -- mk021394 --                                               *vv = v;
  503. // -- mk021394 --                                               return;
  504. // -- mk021394 --                                       }
  505. // -- mk021394 --
  506. // -- mk021394 --                                       if (base_seg->sides[base_common_side].tri_edge == 0) {  // triangulated 012, 023, so if f==0, *vv = v, if f==1, *vv = v if v=0, else v+1
  507. // -- mk021394 --                                               if ((f == 1) && (v > 0))
  508. // -- mk021394 --                                                       v++;
  509. // -- mk021394 --                                               *vv = v;
  510. // -- mk021394 --                                               return;
  511. // -- mk021394 --                                       } else {                                                                // triangulated 013, 123
  512. // -- mk021394 --                                               if (f == 0) {
  513. // -- mk021394 --                                                       if (v == 2)
  514. // -- mk021394 --                                                               v++;
  515. // -- mk021394 --                                               } else
  516. // -- mk021394 --                                                       v++;
  517. // -- mk021394 --                                               *vv = v;
  518. // -- mk021394 --                                               return;
  519. // -- mk021394 --                                       }
  520. // -- mk021394 --                               }
  521. // -- mk021394 --               }
  522. // -- mk021394 --       }
  523. // -- mk021394 --
  524. // -- mk021394 --       Assert(0);      // Error -- Couldn't find face:vertex which matched vertex v1 on base_seg:base_common_side
  525. // -- mk021394 -- }
  526.  
  527. //--rotate_uvs-- // --------------------------------------------------------------------------------------------------------------
  528. //--rotate_uvs-- //     Rotate uvl coordinates uva, uvb about their center point by heading
  529. //--rotate_uvs-- void rotate_uvs(uvl *uva, uvl *uvb, vms_vector *rvec)
  530. //--rotate_uvs-- {
  531. //--rotate_uvs--        uvl     uvc, uva1, uvb1;
  532. //--rotate_uvs--
  533. //--rotate_uvs--        uvc.u = (uva->u + uvb->u)/2;
  534. //--rotate_uvs--        uvc.v = (uva->v + uvb->v)/2;
  535. //--rotate_uvs--
  536. //--rotate_uvs--        uva1.u = fixmul(uva->u - uvc.u, rvec->x) - fixmul(uva->v - uvc.v, rvec->z);
  537. //--rotate_uvs--        uva1.v = fixmul(uva->u - uvc.u, rvec->z) + fixmul(uva->v - uvc.v, rvec->x);
  538. //--rotate_uvs--
  539. //--rotate_uvs--        uva->u = uva1.u + uvc.u;
  540. //--rotate_uvs--        uva->v = uva1.v + uvc.v;
  541. //--rotate_uvs--
  542. //--rotate_uvs--        uvb1.u = fixmul(uvb->u - uvc.u, rvec->x) - fixmul(uvb->v - uvc.v, rvec->z);
  543. //--rotate_uvs--        uvb1.v = fixmul(uvb->u - uvc.u, rvec->z) + fixmul(uvb->v - uvc.v, rvec->x);
  544. //--rotate_uvs--
  545. //--rotate_uvs--        uvb->u = uvb1.u + uvc.u;
  546. //--rotate_uvs--        uvb->v = uvb1.v + uvc.v;
  547. //--rotate_uvs-- }
  548.  
  549.  
  550. // --------------------------------------------------------------------------------------------------------------
  551. void med_assign_uvs_to_side(const vmsegptridx_t con_seg, const unsigned con_common_side, const vmsegptr_t base_seg, const unsigned base_common_side, const unsigned abs_id1, const unsigned abs_id2)
  552. {
  553.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  554.         auto &Vertices = LevelSharedVertexState.get_vertices();
  555.         uvl             uv1,uv2;
  556.         int             v,bv1,bv2, vv1, vv2;
  557.         int             cv1=0, cv2=0;
  558.  
  559.         bv1 = -1;       bv2 = -1;
  560.  
  561.         // Find which vertices in segment match abs_id1, abs_id2
  562.         for (v=0; v<MAX_VERTICES_PER_SEGMENT; v++) {
  563.                 if (base_seg->verts[v] == abs_id1)
  564.                         bv1 = v;
  565.                 if (base_seg->verts[v] == abs_id2)
  566.                         bv2 = v;
  567.                 if (con_seg->verts[v] == abs_id1)
  568.                         cv1 = v;
  569.                 if (con_seg->verts[v] == abs_id2)
  570.                         cv2 = v;
  571.         }
  572.  
  573.         //      Now, bv1, bv2 are segment relative vertices in base segment which are the same as absolute vertices abs_id1, abs_id2
  574.         //           cv1, cv2 are segment relative vertices in conn segment which are the same as absolute vertices abs_id1, abs_id2
  575.  
  576.         Assert((bv1 != -1) && (bv2 != -1) && (cv1 != -1) && (cv2 != -1));
  577.  
  578.         //      Now, scan 4 vertices in base side and 4 vertices in connected side.
  579.         //      Set uv1, uv2 to uv coordinates from base side which correspond to vertices bv1, bv2.
  580.         //      Set vv1, vv2 to relative vertex ids (in 0..3) in connecting side which correspond to cv1, cv2
  581.         vv1 = -1;       vv2 = -1;
  582.         auto &base_uvls = base_seg->unique_segment::sides[base_common_side].uvls;
  583.         for (v=0; v<4; v++) {
  584.                 if (bv1 == Side_to_verts[base_common_side][v])
  585.                         uv1 = base_uvls[v];
  586.  
  587.                 if (bv2 == Side_to_verts[base_common_side][v])
  588.                         uv2 = base_uvls[v];
  589.  
  590.                 if (cv1 == Side_to_verts[con_common_side][v])
  591.                         vv1 = v;
  592.  
  593.                 if (cv2 == Side_to_verts[con_common_side][v])
  594.                         vv2 = v;
  595.         }
  596.  
  597.         Assert((uv1.u != uv2.u) || (uv1.v != uv2.v));
  598.         Assert( (vv1 != -1) && (vv2 != -1) );
  599.         auto &vcvertptr = Vertices.vcptr;
  600.         assign_uvs_to_side(vcvertptr, con_seg, con_common_side, &uv1, &uv2, vv1, vv2);
  601. }
  602.  
  603.  
  604. // -----------------------------------------------------------------------------
  605. //      Given a base and a connecting segment, a side on each of those segments and two global vertex ids,
  606. //      determine which side in each of the segments shares those two vertices.
  607. //      This is used to propagate a texture map id to a connecting segment in an expected and desired way.
  608. //      Since we can attach any side of a segment to any side of another segment, and do so in each case in
  609. //      four different rotations (for a total of 6*6*4 = 144 ways), not having this nifty function will cause
  610. //      great confusion.
  611. static void get_side_ids(const vmsegptr_t base_seg, const vmsegptr_t con_seg, int base_side, int con_side, int abs_id1, int abs_id2, int *base_common_side, int *con_common_side)
  612. {
  613.         int             v0;
  614.  
  615.         *base_common_side = -1;
  616.  
  617.         //      Find side in base segment which contains the two global vertex ids.
  618.         range_for (const auto &&es, enumerate(Side_to_verts))
  619.         {
  620.                 if (es.idx != base_side) {
  621.                         auto &base_vp = es.value;
  622.                         for (v0=0; v0<4; v0++)
  623.                                 if (((base_seg->verts[static_cast<int>(base_vp[v0])] == abs_id1) && (base_seg->verts[static_cast<int>(base_vp[(v0+1) % 4])] == abs_id2)) || ((base_seg->verts[static_cast<int>(base_vp[v0])] == abs_id2) && (base_seg->verts[static_cast<int>(base_vp[ (v0+1) % 4])] == abs_id1))) {
  624.                                         Assert(*base_common_side == -1);                // This means two different sides shared the same edge with base_side == impossible!
  625.                                         *base_common_side = es.idx;
  626.                                 }
  627.                 }
  628.         }
  629.  
  630.         // Note: For connecting segment, process vertices in reversed order.
  631.         *con_common_side = -1;
  632.  
  633.         //      Find side in connecting segment which contains the two global vertex ids.
  634.         range_for (const auto &&es, enumerate(Side_to_verts))
  635.         {
  636.                 if (es.idx != con_side) {
  637.                         auto &con_vp = es.value;
  638.                         for (v0=0; v0<4; v0++)
  639.                                 if (((con_seg->verts[static_cast<int>(con_vp[(v0 + 1) % 4])] == abs_id1) && (con_seg->verts[static_cast<int>(con_vp[v0])] == abs_id2)) || ((con_seg->verts[static_cast<int>(con_vp[(v0 + 1) % 4])] == abs_id2) && (con_seg->verts[static_cast<int>(con_vp[v0])] == abs_id1))) {
  640.                                         Assert(*con_common_side == -1);         // This means two different sides shared the same edge with con_side == impossible!
  641.                                         *con_common_side = es.idx;
  642.                                 }
  643.                 }
  644.         }
  645.  
  646.         Assert((*base_common_side != -1) && (*con_common_side != -1));
  647. }
  648.  
  649. // -----------------------------------------------------------------------------
  650. //      Propagate texture map u,v coordinates from base_seg:base_side to con_seg:con_side.
  651. //      The two vertices abs_id1 and abs_id2 are the only two vertices common to the two sides.
  652. //      If uv_only_flag is 1, then don't assign texture map ids, only update the uv coordinates
  653. //      If uv_only_flag is -1, then ONLY assign texture map ids, don't update the uv coordinates
  654. static void propagate_tmaps_to_segment_side(const vmsegptridx_t base_seg, int base_side, const vmsegptridx_t con_seg, int con_side, int abs_id1, int abs_id2, int uv_only_flag)
  655. {
  656.         int             base_common_side,con_common_side;
  657.         int             tmap_num;
  658.  
  659.         Assert ((uv_only_flag == -1) || (uv_only_flag == 0) || (uv_only_flag == 1));
  660.  
  661.         // Set base_common_side = side in base_seg which contains edge abs_id1:abs_id2
  662.         // Set con_common_side = side in con_seg which contains edge abs_id1:abs_id2
  663.         if (base_seg != con_seg)
  664.                 get_side_ids(base_seg, con_seg, base_side, con_side, abs_id1, abs_id2, &base_common_side, &con_common_side);
  665.         else {
  666.                 base_common_side = base_side;
  667.                 con_common_side = con_side;
  668.         }
  669.  
  670.         // Now, all faces in con_seg which are on side con_common_side get their tmap_num set to whatever tmap is assigned
  671.         // to whatever face I find which is on side base_common_side.
  672.         // First, find tmap_num for base_common_side.  If it doesn't exist (ie, there is a connection there), look at the segment
  673.         // that is connected through it.
  674.         if (!IS_CHILD(con_seg->children[con_common_side])) {
  675.                 if (!IS_CHILD(base_seg->children[base_common_side])) {
  676.                         // There is at least one face here, so get the tmap_num from there.
  677.                         tmap_num = base_seg->unique_segment::sides[base_common_side].tmap_num;
  678.  
  679.                         // Now assign all faces in the connecting segment on side con_common_side to tmap_num.
  680.                         if ((uv_only_flag == -1) || (uv_only_flag == 0))
  681.                                 con_seg->unique_segment::sides[con_common_side].tmap_num = tmap_num;
  682.  
  683.                         if (uv_only_flag != -1)
  684.                                 med_assign_uvs_to_side(con_seg, con_common_side, base_seg, base_common_side, abs_id1, abs_id2);
  685.  
  686.                 } else {                        // There are no faces here, there is a connection, trace through the connection.
  687.                         const auto &&csegp = base_seg.absolute_sibling(base_seg->children[base_common_side]);
  688.                         auto cside = find_connect_side(base_seg, csegp);
  689.                         propagate_tmaps_to_segment_side(csegp, cside, con_seg, con_side, abs_id1, abs_id2, uv_only_flag);
  690.                 }
  691.         }
  692.  
  693. }
  694.  
  695. }
  696.  
  697. namespace dcx {
  698.  
  699. constexpr int8_t Edge_between_sides[MAX_SIDES_PER_SEGMENT][MAX_SIDES_PER_SEGMENT][2] = {
  700. //              left            top             right           bottom  back            front
  701.         { {-1,-1}, { 3, 7}, {-1,-1}, { 2, 6}, { 6, 7}, { 2, 3} },       // left
  702.         { { 3, 7}, {-1,-1}, { 0, 4}, {-1,-1}, { 4, 7}, { 0, 3} },       // top
  703.         { {-1,-1}, { 0, 4}, {-1,-1}, { 1, 5}, { 4, 5}, { 0, 1} },       // right
  704.         { { 2, 6}, {-1,-1}, { 1, 5}, {-1,-1}, { 5, 6}, { 1, 2} },       // bottom
  705.         { { 6, 7}, { 4, 7}, { 4, 5}, { 5, 6}, {-1,-1}, {-1,-1} },       // back
  706.         { { 2, 3}, { 0, 3}, { 0, 1}, { 1, 2}, {-1,-1}, {-1,-1} }};      // front
  707.  
  708. }
  709.  
  710. namespace dsx {
  711.  
  712. // -----------------------------------------------------------------------------
  713. //      Propagate texture map u,v coordinates to base_seg:back_side from base_seg:some-other-side
  714. //      There is no easy way to figure out which side is adjacent to another side along some edge, so we do a bit of searching.
  715. void med_propagate_tmaps_to_back_side(const vmsegptridx_t base_seg, int back_side, int uv_only_flag)
  716. {
  717.         int     v1=0,v2=0;
  718.  
  719.         if (IS_CHILD(base_seg->children[back_side]))
  720.                 return;         // connection, so no sides here.
  721.  
  722.         //      Scan all sides, look for an occupied side which is not back_side or Side_opposite[back_side]
  723.         range_for (const auto &&es, enumerate(Edge_between_sides))
  724.         {
  725.                 const auto s = es.idx;
  726.                 if ((s != back_side) && (s != Side_opposite[back_side])) {
  727.                         auto &ebs = es.value;
  728.                         v1 = ebs[back_side][0];
  729.                         v2 = ebs[back_side][1];
  730.                         propagate_tmaps_to_segment_side(base_seg, s, base_seg, back_side, base_seg->verts[v1], base_seg->verts[v2], uv_only_flag);
  731.                         goto found1;
  732.                 }
  733.         }
  734.         Assert(0);              // Error -- couldn't find edge != back_side and Side_opposite[back_side]
  735. found1: ;
  736.         Assert( (v1 != -1) && (v2 != -1));              // This means there was no shared edge between the two sides.
  737.  
  738.         //      Assign an unused tmap id to the back side.
  739.         //      Note that this can get undone by the caller if this was not part of a new attach, but a rotation or a scale (which
  740.         //      both do attaches).
  741.         //      First see if tmap on back side is anywhere else.
  742.         if (!uv_only_flag) {
  743.                 const auto back_side_tmap = base_seg->unique_segment::sides[back_side].tmap_num;
  744.                 range_for (const auto &&es, enumerate(base_seg->unique_segment::sides))
  745.                 {
  746.                         if (es.idx != back_side)
  747.                                 if (es.value.tmap_num == back_side_tmap)
  748.                                 {
  749.                                         range_for (const uint_fast32_t tmap_num, xrange(MAX_SIDES_PER_SEGMENT))
  750.                                         {
  751.                                                 range_for (const uint_fast32_t ss, xrange(MAX_SIDES_PER_SEGMENT))
  752.                                                         if (ss != back_side)
  753.                                                                 if (base_seg->unique_segment::sides[ss].tmap_num == New_segment.unique_segment::sides[tmap_num].tmap_num)
  754.                                                                         goto found2;            // current texture map (tmap_num) is used on current (ss) side, so try next one
  755.                                                 // Current texture map (tmap_num) has not been used, assign to all faces on back_side.
  756.                                                 base_seg->unique_segment::sides[back_side].tmap_num = New_segment.unique_segment::sides[tmap_num].tmap_num;
  757.                                                 goto done1;
  758.                                         found2: ;
  759.                                         }
  760.                                 }
  761.                 }
  762.         done1: ;
  763.         }
  764.  
  765. }
  766.  
  767. int fix_bogus_uvs_on_side(void)
  768. {
  769.         med_propagate_tmaps_to_back_side(Cursegp, Curside, 1);
  770.         return 0;
  771. }
  772.  
  773. static void fix_bogus_uvs_on_side1(const vmsegptridx_t sp, const unsigned sidenum, const int uvonly_flag)
  774. {
  775.         auto &uvls = sp->unique_segment::sides[sidenum].uvls;
  776.         if (uvls[0].u == 0 && uvls[1].u == 0 && uvls[2].u == 0)
  777.         {
  778.                 med_propagate_tmaps_to_back_side(sp, sidenum, uvonly_flag);
  779.         }
  780. }
  781.  
  782. static void fix_bogus_uvs_seg(const vmsegptridx_t segp)
  783. {
  784.         range_for (const auto &&es, enumerate(segp->children))
  785.         {
  786.                 if (!IS_CHILD(es.value))
  787.                         fix_bogus_uvs_on_side1(segp, es.idx, 1);
  788.         }
  789. }
  790.  
  791. int fix_bogus_uvs_all(void)
  792. {
  793.         range_for (const auto &&segp, vmsegptridx)
  794.         {
  795.                 if (segp->segnum != segment_none)
  796.                         fix_bogus_uvs_seg(segp);
  797.         }
  798.         return 0;
  799. }
  800.  
  801. // -----------------------------------------------------------------------------
  802. //      Segment base_seg is connected through side base_side to segment con_seg on con_side.
  803. //      For all walls in con_seg, find the wall in base_seg which shares an edge.  Copy tmap_num
  804. //      from that side in base_seg to the wall in con_seg.  If the wall in base_seg is not present
  805. //      (ie, there is another segment connected through it), follow the connection through that
  806. //      segment to get the wall in the connected segment which shares the edge, and get tmap_num from there.
  807. static void propagate_tmaps_to_segment_sides(const vmsegptridx_t base_seg, int base_side, const vmsegptridx_t con_seg, int con_side, int uv_only_flag)
  808. {
  809.         int             abs_id1,abs_id2;
  810.         int             v;
  811.  
  812.         auto &base_vp = Side_to_verts[base_side];
  813.  
  814.         // Do for each edge on connecting face.
  815.         for (v=0; v<4; v++) {
  816.                 abs_id1 = base_seg->verts[static_cast<int>(base_vp[v])];
  817.                 abs_id2 = base_seg->verts[static_cast<int>(base_vp[(v+1) % 4])];
  818.                 propagate_tmaps_to_segment_side(base_seg, base_side, con_seg, con_side, abs_id1, abs_id2, uv_only_flag);
  819.         }
  820.  
  821. }
  822.  
  823. // -----------------------------------------------------------------------------
  824. //      Propagate texture maps in base_seg to con_seg.
  825. //      For each wall in con_seg, find the wall in base_seg which shared an edge.  Copy tmap_num from that
  826. //      wall in base_seg to the wall in con_seg.  If the wall in base_seg is not present, then look at the
  827. //      segment connected through base_seg through the wall.  The wall with a common edge is the new wall
  828. //      of interest.  Continue searching in this way until a wall of interest is present.
  829. void med_propagate_tmaps_to_segments(const vmsegptridx_t base_seg,const vmsegptridx_t con_seg, int uv_only_flag)
  830. {
  831.         range_for (const auto &&es, enumerate(base_seg->children))
  832.                 if (es.value == con_seg)
  833.                         propagate_tmaps_to_segment_sides(base_seg, es.idx, con_seg, find_connect_side(base_seg, con_seg), uv_only_flag);
  834.  
  835.         con_seg->static_light = base_seg->static_light;
  836.  
  837.         validate_uv_coordinates(con_seg);
  838. }
  839.  
  840.  
  841. // -------------------------------------------------------------------------------
  842. //      Copy texture map uvs from srcseg to destseg.
  843. //      If two segments have different face structure (eg, destseg has two faces on side 3, srcseg has only 1)
  844. //      then assign uvs according to side vertex id, not face vertex id.
  845. void copy_uvs_seg_to_seg(unique_segment &destseg, const unique_segment &srcseg)
  846. {
  847.         range_for (const auto &&z, zip(destseg.sides, srcseg.sides))
  848.         {
  849.                 auto &ds = std::get<0>(z);
  850.                 auto &ss = std::get<1>(z);
  851.                 ds.tmap_num = ss.tmap_num;
  852.                 ds.tmap_num2 = ss.tmap_num2;
  853.         }
  854.  
  855.         destseg.static_light = srcseg.static_light;
  856. }
  857.  
  858. }
  859.  
  860. namespace dcx {
  861.  
  862. //      _________________________________________________________________________________________________________________________
  863. //      Maximum distance between a segment containing light to a segment to receive light.
  864. #define LIGHT_DISTANCE_THRESHOLD        (F1_0*80)
  865. fix     Magical_light_constant = (F1_0*16);
  866.  
  867. // int  Seg0, Seg1;
  868.  
  869. //int   Bugseg = 27;
  870.  
  871. struct hash_info {
  872.         sbyte                   flag, hit_type;
  873.         vms_vector      vector;
  874. };
  875.  
  876. #define FVI_HASH_SIZE 8
  877. #define FVI_HASH_AND_MASK (FVI_HASH_SIZE - 1)
  878.  
  879. //      Note: This should be malloced.
  880. //                      Also, the vector should not be 12 bytes, you should only care about some smaller portion of it.
  881. static std::array<hash_info, FVI_HASH_SIZE> fvi_cache;
  882. static int Hash_hits=0, Hash_retries=0, Hash_calcs=0;
  883.  
  884. //      -----------------------------------------------------------------------------------------
  885. //      Set light from a light source.
  886. //      Light incident on a surface is defined by the light incident at its points.
  887. //      Light at a point = K * (V . N) / d
  888. //      where:
  889. //              K = some magical constant to make everything look good
  890. //              V = normalized vector from light source to point
  891. //              N = surface normal at point
  892. //              d = distance from light source to point
  893. //      (Note that the above equation can be simplified to K * (VV . N) / d^2 where VV = non-normalized V)
  894. //      Light intensity emitted from a light source is defined to be cast from four points.
  895. //      These four points are 1/64 of the way from the corners of the light source to the center
  896. //      of its segment.  By assuming light is cast from these points, rather than from on the
  897. //      light surface itself, light will be properly cast on the light surface.  Otherwise, the
  898. //      vector V would be the null vector.
  899. //      If quick_light set, then don't use find_vector_intersection
  900. static void cast_light_from_side(const vmsegptridx_t segp, int light_side, fix light_intensity, int quick_light)
  901. {
  902.         int                     vertnum;
  903.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  904.         auto &Vertices = LevelSharedVertexState.get_vertices();
  905.         auto &vcvertptr = Vertices.vcptr;
  906.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  907.         auto &vcwallptr = Walls.vcptr;
  908.         const auto segment_center = compute_segment_center(vcvertptr, segp);
  909.         //      Do for four lights, one just inside each corner of side containing light.
  910.         range_for (const auto lightnum, Side_to_verts[light_side])
  911.         {
  912.                 // fix                  inverse_segment_magnitude;
  913.  
  914.                 const auto light_vertex_num = segp->verts[lightnum];
  915.                 auto light_location = *vcvertptr(light_vertex_num);
  916.  
  917.         //      New way, 5/8/95: Move towards center irrespective of size of segment.
  918.                 const auto vector_to_center = vm_vec_normalized_quick(vm_vec_sub(segment_center, light_location));
  919.         vm_vec_add2(light_location, vector_to_center);
  920.  
  921. // -- Old way, before 5/8/95 --         // -- This way was kind of dumb.  In larger segments, you move LESS towards the center.
  922. // -- Old way, before 5/8/95 --         //    Main problem, though, is vertices don't illuminate themselves well in oblong segments because the dot product is small.
  923. // -- Old way, before 5/8/95 --         vm_vec_sub(&vector_to_center, &segment_center, &light_location);
  924. // -- Old way, before 5/8/95 --         inverse_segment_magnitude = fixdiv(F1_0/5, vm_vec_mag(&vector_to_center));
  925. // -- Old way, before 5/8/95 --         vm_vec_scale_add(&light_location, &light_location, &vector_to_center, inverse_segment_magnitude);
  926.  
  927.                 range_for (const auto &&rsegp, vmsegptr)
  928.                 {
  929.                         fix                     dist_to_rseg;
  930.  
  931.                         range_for (auto &i, fvi_cache)
  932.                                 i.flag = 0;
  933.  
  934.                         //      efficiency hack (I hope!), for faraway segments, don't check each point.
  935.                         const auto r_segment_center = compute_segment_center(vcvertptr, rsegp);
  936.                         dist_to_rseg = vm_vec_dist_quick(r_segment_center, segment_center);
  937.  
  938.                         if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
  939.                                 for (const auto &&[sidenum, srside, urside] : enumerate(zip(rsegp->shared_segment::sides, rsegp->unique_segment::sides)))
  940.                                 {
  941.                                         if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, rsegp, sidenum) != WID_NO_WALL)
  942.                                         {
  943.                                                 auto &side_normalp = srside.normals[0]; //      kinda stupid? always use vector 0.
  944.  
  945.                                                 for (vertnum=0; vertnum<4; vertnum++) {
  946.                                                         fix                     distance_to_point, light_at_point, light_dot;
  947.  
  948.                                                         const auto abs_vertnum = rsegp->verts[Side_to_verts[sidenum][vertnum]];
  949.                                                         vms_vector vert_location = *vcvertptr(abs_vertnum);
  950.                                                         distance_to_point = vm_vec_dist_quick(vert_location, light_location);
  951.                                                         const auto vector_to_light = vm_vec_normalized(vm_vec_sub(light_location, vert_location));
  952.  
  953.                                                         //      Hack: In oblong segments, it's possible to get a very small dot product
  954.                                                         //      but the light source is very nearby (eg, illuminating light itself!).
  955.                                                         light_dot = vm_vec_dot(vector_to_light, side_normalp);
  956.                                                         if (distance_to_point < F1_0)
  957.                                                                 if (light_dot > 0)
  958.                                                                         light_dot = (light_dot + F1_0)/2;
  959.  
  960.                                                         if (light_dot > 0) {
  961.                                                                 light_at_point = fixdiv(fixmul(light_dot, light_dot), distance_to_point);
  962.                                                                 light_at_point = fixmul(light_at_point, Magical_light_constant);
  963.                                                                 if (light_at_point >= 0) {
  964.                                                                         fvi_info        hit_data;
  965.                                                                         int             hit_type;
  966.                                                                         fix             inverse_segment_magnitude;
  967.  
  968.                                                                         const auto r_vector_to_center = vm_vec_sub(r_segment_center, vert_location);
  969.                                                                         inverse_segment_magnitude = fixdiv(F1_0/3, vm_vec_mag(r_vector_to_center));
  970.                                                                         const auto vert_location_1 = vm_vec_scale_add(vert_location, r_vector_to_center, inverse_segment_magnitude);
  971.                                                                         vert_location = vert_location_1;
  972.  
  973. //if ((segp-Segments == 199) && (rsegp-Segments==199))
  974. //      Int3();
  975. // Seg0 = segp-Segments;
  976. // Seg1 = rsegp-Segments;
  977.                                                                         if (!quick_light) {
  978.                                                                                 int hash_value = Side_to_verts[sidenum][vertnum];
  979.                                                                                 hash_info       *hashp = &fvi_cache[hash_value];
  980.                                                                                 while (1) {
  981.                                                                                         if (hashp->flag) {
  982.                                                                                                 if ((hashp->vector.x == vector_to_light.x) && (hashp->vector.y == vector_to_light.y) && (hashp->vector.z == vector_to_light.z)) {
  983.                                                                                                         hit_type = hashp->hit_type;
  984.                                                                                                         Hash_hits++;
  985.                                                                                                         break;
  986.                                                                                                 } else {
  987.                                                                                                         Int3(); // How is this possible?  Should be no hits!
  988.                                                                                                         Hash_retries++;
  989.                                                                                                         hash_value = (hash_value+1) & FVI_HASH_AND_MASK;
  990.                                                                                                         hashp = &fvi_cache[hash_value];
  991.                                                                                                 }
  992.                                                                                         } else {
  993.                                                                                                 fvi_query fq;
  994.  
  995.                                                                                                 Hash_calcs++;
  996.                                                                                                 hashp->vector = vector_to_light;
  997.                                                                                                 hashp->flag = 1;
  998.  
  999.                                                                                                 fq.p0                                           = &light_location;
  1000.                                                                                                 fq.startseg                             = segp;
  1001.                                                                                                 fq.p1                                           = &vert_location;
  1002.                                                                                                 fq.rad                                  = 0;
  1003.                                                                                                 fq.thisobjnum                   = object_none;
  1004.                                                                                                 fq.ignore_obj_list.first = nullptr;
  1005.                                                                                                 fq.flags                                        = 0;
  1006.  
  1007.                                                                                                 hit_type = find_vector_intersection(fq, hit_data);
  1008.                                                                                                 hashp->hit_type = hit_type;
  1009.                                                                                                 break;
  1010.                                                                                         }
  1011.                                                                                 }
  1012.                                                                         } else
  1013.                                                                                 hit_type = HIT_NONE;
  1014.                                                                         switch (hit_type) {
  1015.                                                                                 case HIT_NONE:
  1016.                                                                                         light_at_point = fixmul(light_at_point, light_intensity);
  1017.                                                                                         urside.uvls[vertnum].l += light_at_point;
  1018.                                                                                         if (urside.uvls[vertnum].l > F1_0)
  1019.                                                                                                 urside.uvls[vertnum].l = F1_0;
  1020.                                                                                         break;
  1021.                                                                                 case HIT_WALL:
  1022.                                                                                         break;
  1023.                                                                                 case HIT_OBJECT:
  1024.                                                                                         Int3(); // Hit object, should be ignoring objects!
  1025.                                                                                         break;
  1026.                                                                                 case HIT_BAD_P0:
  1027.                                                                                         Int3(); //      Ugh, this thing again, what happened, what does it mean?
  1028.                                                                                         break;
  1029.                                                                         }
  1030.                                                                 }       //      end if (light_at_point...
  1031.                                                         }       // end if (light_dot >...
  1032.                                                 }       //      end for (vertnum=0...
  1033.                                         }       //      end if (rsegp...
  1034.                                 }       //      end for (sidenum=0...
  1035.                         }       //      end if (dist_to_rseg...
  1036.  
  1037.                 }       //      end for (segnum=0...
  1038.  
  1039.         }       //      end for (lightnum=0...
  1040. }
  1041.  
  1042.  
  1043. //      ------------------------------------------------------------------------------------------
  1044. //      Zero all lighting values.
  1045. static void calim_zero_light_values(void)
  1046. {
  1047.         range_for (unique_segment &segp, vmsegptr)
  1048.         {
  1049.                 range_for (auto &side, segp.sides)
  1050.                 {
  1051.                         range_for (auto &uvl, side.uvls)
  1052.                                 uvl.l = F1_0/64;        // Put a tiny bit of light here.
  1053.                 }
  1054.                 segp.static_light = F1_0 / 64;
  1055.         }
  1056. }
  1057.  
  1058.  
  1059. //      ------------------------------------------------------------------------------------------
  1060. //      Used in setting average light value in a segment, cast light from a side to the center
  1061. //      of all segments.
  1062. static void cast_light_from_side_to_center(const vmsegptridx_t segp, int light_side, fix light_intensity, int quick_light)
  1063. {
  1064.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1065.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1066.         auto &vcvertptr = Vertices.vcptr;
  1067.         const auto &&segment_center = compute_segment_center(vcvertptr, segp);
  1068.         //      Do for four lights, one just inside each corner of side containing light.
  1069.         range_for (const auto lightnum, Side_to_verts[light_side])
  1070.         {
  1071.                 const auto light_vertex_num = segp->verts[lightnum];
  1072.                 auto &vert_light_location = *vcvertptr(light_vertex_num);
  1073.                 const auto vector_to_center = vm_vec_sub(segment_center, vert_light_location);
  1074.                 const auto light_location = vm_vec_scale_add(vert_light_location, vector_to_center, F1_0/64);
  1075.  
  1076.                 range_for (const auto &&rsegp, vmsegptr)
  1077.                 {
  1078.                         fix                     dist_to_rseg;
  1079. //if ((segp == &Segments[Bugseg]) && (rsegp == &Segments[Bugseg]))
  1080. //      Int3();
  1081.                         const auto r_segment_center = compute_segment_center(vcvertptr, rsegp);
  1082.                         dist_to_rseg = vm_vec_dist_quick(r_segment_center, segment_center);
  1083.  
  1084.                         if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
  1085.                                 fix     light_at_point;
  1086.                                 if (dist_to_rseg > F1_0)
  1087.                                         light_at_point = fixdiv(Magical_light_constant, dist_to_rseg);
  1088.                                 else
  1089.                                         light_at_point = Magical_light_constant;
  1090.  
  1091.                                 if (light_at_point >= 0) {
  1092.                                         int             hit_type;
  1093.  
  1094.                                         if (!quick_light) {
  1095.                                                 fvi_query fq;
  1096.                                                 fvi_info        hit_data;
  1097.  
  1098.                                                 fq.p0                                           = &light_location;
  1099.                                                 fq.startseg                             = segp;
  1100.                                                 fq.p1                                           = &r_segment_center;
  1101.                                                 fq.rad                                  = 0;
  1102.                                                 fq.thisobjnum                   = object_none;
  1103.                                                 fq.ignore_obj_list.first = nullptr;
  1104.                                                 fq.flags                                        = 0;
  1105.  
  1106.                                                 hit_type = find_vector_intersection(fq, hit_data);
  1107.                                         }
  1108.                                         else
  1109.                                                 hit_type = HIT_NONE;
  1110.  
  1111.                                         switch (hit_type) {
  1112.                                                 case HIT_NONE:
  1113.                                                         light_at_point = fixmul(light_at_point, light_intensity);
  1114.                                                         if (light_at_point >= F1_0)
  1115.                                                                 light_at_point = F1_0-1;
  1116.                                                         rsegp->static_light += light_at_point;
  1117.                                                         if (segp->static_light < 0)     // if it went negative, saturate
  1118.                                                                 segp->static_light = 0;
  1119.                                                         break;
  1120.                                                 case HIT_WALL:
  1121.                                                         break;
  1122.                                                 case HIT_OBJECT:
  1123.                                                         Int3(); // Hit object, should be ignoring objects!
  1124.                                                         break;
  1125.                                                 case HIT_BAD_P0:
  1126.                                                         Int3(); //      Ugh, this thing again, what happened, what does it mean?
  1127.                                                         break;
  1128.                                         }
  1129.                                 }       //      end if (light_at_point...
  1130.                         }       //      end if (dist_to_rseg...
  1131.  
  1132.                 }       //      end for (segnum=0...
  1133.  
  1134.         }       //      end for (lightnum=0...
  1135.  
  1136. }
  1137.  
  1138. //      ------------------------------------------------------------------------------------------
  1139. //      Process all lights.
  1140. static void calim_process_all_lights(int quick_light)
  1141. {
  1142.         auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
  1143.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1144.         auto &vcwallptr = Walls.vcptr;
  1145.         range_for (const auto &&segp, vmsegptridx)
  1146.         {
  1147.                 range_for (const auto &&es, enumerate(segp->unique_segment::sides))
  1148.                 {
  1149.                         const uint_fast32_t sidenum = es.idx;
  1150.                         if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, sidenum) != WID_NO_WALL)
  1151.                         {
  1152.                                 const auto sidep = &es.value;
  1153.                                 fix     light_intensity;
  1154.  
  1155.                                 light_intensity = TmapInfo[sidep->tmap_num].lighting + TmapInfo[sidep->tmap_num2 & 0x3fff].lighting;
  1156.  
  1157. //                              if (segp->sides[sidenum].wall_num != -1) {
  1158. //                                      int     wall_num, bitmap_num, effect_num;
  1159. //                                      wall_num = segp->sides[sidenum].wall_num;
  1160. //                                      effect_num = Walls[wall_num].type;
  1161. //                                      bitmap_num = effects_bm_num[effect_num];
  1162. //
  1163. //                                      light_intensity += TmapInfo[bitmap_num].lighting;
  1164. //                              }
  1165.  
  1166.                                 if (light_intensity) {
  1167.                                         light_intensity /= 4;                   // casting light from four spots, so divide by 4.
  1168.                                         cast_light_from_side(segp, sidenum, light_intensity, quick_light);
  1169.                                         cast_light_from_side_to_center(segp, sidenum, light_intensity, quick_light);
  1170.                                 }
  1171.                         }
  1172.                 }
  1173.         }
  1174. }
  1175.  
  1176. //      ------------------------------------------------------------------------------------------
  1177. //      Apply static light in mine.
  1178. //      First, zero all light values.
  1179. //      Then, for all light sources, cast their light.
  1180. static void cast_all_light_in_mine(int quick_flag)
  1181. {
  1182.         validate_segment_all(LevelSharedSegmentState);
  1183.         calim_zero_light_values();
  1184.  
  1185.         calim_process_all_lights(quick_flag);
  1186. }
  1187.  
  1188. }
  1189.  
  1190. // int  Fvit_num = 1000;
  1191. //
  1192. // fix find_vector_intersection_test(void)
  1193. // {
  1194. //      int             i;
  1195. //      fvi_info        hit_data;
  1196. //      int             p0_seg, p1_seg, this_objnum, ignore_obj, check_obj_flag;
  1197. //      fix             rad;
  1198. //      int             start_time = timer_get_milliseconds();;
  1199. //      vms_vector      p0,p1;
  1200. //
  1201. //      ignore_obj = 1;
  1202. //      check_obj_flag = 0;
  1203. //      this_objnum = -1;
  1204. //      rad = F1_0/4;
  1205. //
  1206. //      for (i=0; i<Fvit_num; i++) {
  1207. //              p0_seg = d_rand()*(Highest_segment_index+1)/32768;
  1208. //              compute_segment_center(&p0, &Segments[p0_seg]);
  1209. //
  1210. //              p1_seg = d_rand()*(Highest_segment_index+1)/32768;
  1211. //              compute_segment_center(&p1, &Segments[p1_seg]);
  1212. //
  1213. //              find_vector_intersection(&hit_data, &p0, p0_seg, &p1, rad, this_objnum, ignore_obj, check_obj_flag);
  1214. //      }
  1215. //
  1216. //      return timer_get_milliseconds() - start_time;
  1217. // }
  1218.