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.  * Functions moved from segment.c to make editor separable from game.
  23.  *
  24.  */
  25.  
  26. #include <algorithm>
  27. #include <cassert>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include <string.h>     //      for memset()
  31.  
  32. #include "u_mem.h"
  33. #include "inferno.h"
  34. #include "game.h"
  35. #include "dxxerror.h"
  36. #include "console.h"
  37. #include "vecmat.h"
  38. #include "gameseg.h"
  39. #include "gameseq.h"
  40. #include "wall.h"
  41. #include "fuelcen.h"
  42. #include "textures.h"
  43. #include "fvi.h"
  44. #include "object.h"
  45. #include "byteutil.h"
  46. #include "lighting.h"
  47. #include "mission.h"
  48. #if DXX_USE_EDITOR
  49. #include "editor/editor.h"
  50. #endif
  51.  
  52. #include "compiler-range_for.h"
  53. #include "d_range.h"
  54. #include "cast_range_result.h"
  55.  
  56. using std::min;
  57.  
  58. namespace {
  59.  
  60.         /* The array can be of any type that can hold values in the range
  61.          * [0, AMBIENT_SEGMENT_DEPTH].
  62.          */
  63. struct segment_lava_depth_array : std::array<uint8_t, MAX_SEGMENTS> {};
  64. struct segment_water_depth_array : std::array<uint8_t, MAX_SEGMENTS> {};
  65.  
  66. class abs_vertex_lists_predicate
  67. {
  68.         const std::array<unsigned, MAX_VERTICES_PER_SEGMENT> &m_vp;
  69.         const std::array<unsigned, 4> &m_sv;
  70. public:
  71.         abs_vertex_lists_predicate(const shared_segment &seg, const uint_fast32_t sidenum) :
  72.                 m_vp(seg.verts), m_sv(Side_to_verts_int[sidenum])
  73.         {
  74.         }
  75.         unsigned operator()(const uint_fast32_t vv) const
  76.         {
  77.                 return m_vp[m_sv[vv]];
  78.         }
  79. };
  80.  
  81. class all_vertnum_lists_predicate : public abs_vertex_lists_predicate
  82. {
  83. public:
  84.         using abs_vertex_lists_predicate::abs_vertex_lists_predicate;
  85.         vertex_vertnum_pair operator()(const uint_fast32_t vv) const
  86.         {
  87.                 return {this->abs_vertex_lists_predicate::operator()(vv), static_cast<unsigned>(vv)};
  88.         }
  89. };
  90.  
  91. struct verts_for_normal
  92. {
  93.         std::array<unsigned, 4> vsorted;
  94.         bool negate_flag;
  95. };
  96.  
  97. constexpr vm_distance fcd_abort_cache_value{F1_0 * 1000};
  98. constexpr vm_distance fcd_abort_return_value{-1};
  99.  
  100. }
  101.  
  102. namespace dcx {
  103.  
  104. // How far a point can be from a plane, and still be "in" the plane
  105. #define PLANE_DIST_TOLERANCE    250
  106.  
  107. static uint_fast32_t find_connect_child(const vcsegidx_t base_seg, const std::array<segnum_t, MAX_SIDES_PER_SEGMENT> &children)
  108. {
  109.         const auto &&b = begin(children);
  110.         return std::distance(b, std::find(b, end(children), base_seg));
  111. }
  112.  
  113. static void compute_center_point_on_side(fvcvertptr &vcvertptr, vms_vector &r, const std::array<unsigned, MAX_VERTICES_PER_SEGMENT> &verts, const unsigned side)
  114. {
  115.         vms_vector vp;
  116.         vm_vec_zero(vp);
  117.         range_for (auto &v, Side_to_verts[side])
  118.                 vm_vec_add2(vp, vcvertptr(verts[v]));
  119.         vm_vec_copy_scale(r, vp, F1_0 / 4);
  120. }
  121.  
  122. static void compute_segment_center(fvcvertptr &vcvertptr, vms_vector &r, const std::array<unsigned, MAX_VERTICES_PER_SEGMENT> &verts)
  123. {
  124.         vms_vector vp;
  125.         vm_vec_zero(vp);
  126.         range_for (auto &v, verts)
  127.                 vm_vec_add2(vp, vcvertptr(v));
  128.         vm_vec_copy_scale(r, vp, F1_0 / 8);
  129. }
  130.  
  131. // ------------------------------------------------------------------------------------------
  132. // Compute the center point of a side of a segment.
  133. //      The center point is defined to be the average of the 4 points defining the side.
  134. void compute_center_point_on_side(fvcvertptr &vcvertptr, vms_vector &vp, const shared_segment &sp, const unsigned side)
  135. {
  136.         compute_center_point_on_side(vcvertptr, vp, sp.verts, side);
  137. }
  138.  
  139. // ------------------------------------------------------------------------------------------
  140. // Compute segment center.
  141. //      The center point is defined to be the average of the 8 points defining the segment.
  142. void compute_segment_center(fvcvertptr &vcvertptr, vms_vector &vp, const shared_segment &sp)
  143. {
  144.         compute_segment_center(vcvertptr, vp, sp.verts);
  145. }
  146.  
  147. // -----------------------------------------------------------------------------
  148. //      Given two segments, return the side index in the connecting segment which connects to the base segment
  149. //      Optimized by MK on 4/21/94 because it is a 2% load.
  150. uint_fast32_t find_connect_side(const vcsegidx_t base_seg, const shared_segment &con_seg)
  151. {
  152.         return find_connect_child(base_seg, con_seg.children);
  153. }
  154.  
  155. // -----------------------------------------------------------------------------------
  156. //      Given a side, return the number of faces
  157. bool get_side_is_quad(const shared_side &sidep)
  158. {
  159.         switch (sidep.get_type())
  160.         {
  161.                 case side_type::quad:
  162.                         return true;
  163.                 case side_type::tri_02:
  164.                 case side_type::tri_13:
  165.                         return false;
  166.                 default:
  167.                         throw shared_side::illegal_type(sidep);
  168.         }
  169. }
  170.  
  171. // Fill in array with four absolute point numbers for a given side
  172. static void get_side_verts(side_vertnum_list_t &vertlist, const std::array<unsigned, MAX_VERTICES_PER_SEGMENT> &vp, const unsigned sidenum)
  173. {
  174.         auto &sv = Side_to_verts[sidenum];
  175.         for (unsigned i = 4; i--;)
  176.                 vertlist[i] = vp[sv[i]];
  177. }
  178.  
  179. void get_side_verts(side_vertnum_list_t &vertlist, const shared_segment &segp, const unsigned sidenum)
  180. {
  181.         get_side_verts(vertlist, segp.verts, sidenum);
  182. }
  183.  
  184. }
  185.  
  186. namespace dsx {
  187.  
  188. __attribute_cold
  189. __noreturn
  190. static void create_vertex_list_from_invalid_side(const shared_segment &segp, const shared_side &sidep)
  191. {
  192.         throw shared_side::illegal_type(segp, sidep);
  193. }
  194.  
  195. template <typename T, typename V>
  196. static uint_fast32_t create_vertex_lists_from_values(T &va, const shared_segment &segp, const shared_side &sidep, const V &&f0, const V &&f1, const V &&f2, const V &&f3)
  197. {
  198.         const auto type = sidep.get_type();
  199.         if (type == side_type::tri_13)
  200.         {
  201.                 va[0] = va[5] = f3;
  202.                 va[1] = f0;
  203.                 va[2] = va[3] = f1;
  204.                 va[4] = f2;
  205.                 return 2;
  206.         }
  207.         va[0] = f0;
  208.         va[1] = f1;
  209.         va[2] = f2;
  210.         switch (type)
  211.         {
  212.                 case side_type::quad:
  213.                         va[3] = f3;
  214.                         /* Unused, but required to prevent bogus
  215.                          * -Wmaybe-uninitialized in check_segment_connections
  216.                          */
  217.                         va[4] = va[5] = {};
  218.                         DXX_MAKE_MEM_UNDEFINED(&va[4], 2 * sizeof(va[4]));
  219.                         return 1;
  220.                 case side_type::tri_02:
  221.                         va[3] = f2;
  222.                         va[4] = f3;
  223.                         va[5] = f0;
  224.  
  225.                         //IMPORTANT: DON'T CHANGE THIS CODE WITHOUT CHANGING GET_SEG_MASKS()
  226.                         //CREATE_ABS_VERTEX_LISTS(), CREATE_ALL_VERTEX_LISTS(), CREATE_ALL_VERTNUM_LISTS()
  227.                         return 2;
  228.                 default:
  229.                         create_vertex_list_from_invalid_side(segp, sidep);
  230.         }
  231. }
  232.  
  233. template <typename T, typename F>
  234. static inline uint_fast32_t create_vertex_lists_by_predicate(T &va, const shared_segment &segp, const shared_side &sidep, const F &&f)
  235. {
  236.         return create_vertex_lists_from_values(va, segp, sidep, f(0), f(1), f(2), f(3));
  237. }
  238.  
  239. #if DXX_USE_EDITOR
  240. // -----------------------------------------------------------------------------------
  241. //      Create all vertex lists (1 or 2) for faces on a side.
  242. //      Sets:
  243. //              num_faces               number of lists
  244. //              vertices                        vertices in all (1 or 2) faces
  245. //      If there is one face, it has 4 vertices.
  246. //      If there are two faces, they both have three vertices, so face #0 is stored in vertices 0,1,2,
  247. //      face #1 is stored in vertices 3,4,5.
  248. // Note: these are not absolute vertex numbers, but are relative to the segment
  249. // Note:  for triagulated sides, the middle vertex of each trianle is the one NOT
  250. //   adjacent on the diagonal edge
  251. uint_fast32_t create_all_vertex_lists(vertex_array_list_t &vertices, const shared_segment &segp, const shared_side &sidep, const uint_fast32_t sidenum)
  252. {
  253.         assert(sidenum < Side_to_verts_int.size());
  254.         auto &sv = Side_to_verts_int[sidenum];
  255.         return create_vertex_lists_by_predicate(vertices, segp, sidep, [&sv](const uint_fast32_t vv) {
  256.                 return sv[vv];
  257.         });
  258. }
  259. #endif
  260.  
  261. // -----------------------------------------------------------------------------------
  262. // Like create all vertex lists, but returns the vertnums (relative to
  263. // the side) for each of the faces that make up the side.
  264. //      If there is one face, it has 4 vertices.
  265. //      If there are two faces, they both have three vertices, so face #0 is stored in vertices 0,1,2,
  266. //      face #1 is stored in vertices 3,4,5.
  267. void create_all_vertnum_lists(vertex_vertnum_array_list &vertnums, const shared_segment &segp, const shared_side &sidep, const uint_fast32_t sidenum)
  268. {
  269.         create_vertex_lists_by_predicate(vertnums, segp, sidep, all_vertnum_lists_predicate(segp, sidenum));
  270. }
  271.  
  272. // -----
  273. // like create_all_vertex_lists(), but generate absolute point numbers
  274. uint_fast32_t create_abs_vertex_lists(vertex_array_list_t &vertices, const shared_segment &segp, const shared_side &sidep, const uint_fast32_t sidenum)
  275. {
  276.         return create_vertex_lists_by_predicate(vertices, segp, sidep, abs_vertex_lists_predicate(segp, sidenum));
  277. }
  278.  
  279. //returns 3 different bitmasks with info telling if this sphere is in
  280. //this segment.  See segmasks structure for info on fields  
  281. segmasks get_seg_masks(fvcvertptr &vcvertptr, const vms_vector &checkp, const shared_segment &seg, const fix rad)
  282. {
  283.         int                     sn,facebit,sidebit;
  284.         segmasks                masks{};
  285.  
  286.         //check point against each side of segment. return bitmask
  287.  
  288.         for (sn=0,facebit=sidebit=1;sn<6;sn++,sidebit<<=1) {
  289.                 auto &s = seg.sides[sn];
  290.                
  291.                 // Get number of faces on this side, and at vertex_list, store vertices.
  292.                 //      If one face, then vertex_list indicates a quadrilateral.
  293.                 //      If two faces, then 0,1,2 define one triangle, 3,4,5 define the second.
  294.                 const auto v = create_abs_vertex_lists(seg, s, sn);
  295.                 const auto &num_faces = v.first;
  296.                 const auto &vertex_list = v.second;
  297.  
  298.                 //ok...this is important.  If a side has 2 faces, we need to know if
  299.                 //those faces form a concave or convex side.  If the side pokes out,
  300.                 //then a point is on the back of the side if it is behind BOTH faces,
  301.                 //but if the side pokes in, a point is on the back if behind EITHER face.
  302.  
  303.                 if (num_faces==2) {
  304.                         int     side_count,center_count;
  305.  
  306.                         const auto vertnum = min(vertex_list[0],vertex_list[2]);
  307.                         const auto &&mvert = vcvertptr(vertnum);
  308.  
  309.                         auto a = vertex_list[4] < vertex_list[1]
  310.                                 ? std::make_pair(vertex_list[4], &s.normals[0])
  311.                                 : std::make_pair(vertex_list[1], &s.normals[1]);
  312.                         const auto mdist = vm_dist_to_plane(vcvertptr(a.first), *a.second, mvert);
  313.  
  314.                         side_count = center_count = 0;
  315.  
  316.                         for (int fn=0;fn<2;fn++,facebit<<=1) {
  317.  
  318.                                 const auto dist = vm_dist_to_plane(checkp, s.normals[fn], mvert);
  319.  
  320.                                 if (dist-rad < -PLANE_DIST_TOLERANCE) {
  321.                                         if (dist < -PLANE_DIST_TOLERANCE)       //in front of face
  322.                                                 center_count++;
  323.  
  324.                                         masks.facemask |= facebit;
  325.                                         side_count++;
  326.                                 }
  327.                         }
  328.  
  329.                         if (!(mdist > PLANE_DIST_TOLERANCE)) {          //must be behind both faces
  330.  
  331.                                 if (side_count==2)
  332.                                         masks.sidemask |= sidebit;
  333.  
  334.                                 if (center_count==2)
  335.                                         masks.centermask |= sidebit;
  336.  
  337.                         }
  338.                         else {                                                  //must be behind at least one face
  339.  
  340.                                 if (side_count)
  341.                                         masks.sidemask |= sidebit;
  342.  
  343.                                 if (center_count)
  344.                                         masks.centermask |= sidebit;
  345.  
  346.                         }
  347.  
  348.  
  349.                 }
  350.                 else {                          //only one face on this side
  351.                         //use lowest point number
  352.                         auto b = begin(vertex_list);
  353.                         const auto vertnum = *std::min_element(b, std::next(b, 4));
  354.                         const auto &&mvert = vcvertptr(vertnum);
  355.  
  356.                         const auto dist = vm_dist_to_plane(checkp, s.normals[0], mvert);
  357.                         if (dist-rad < -PLANE_DIST_TOLERANCE) {
  358.                                 if (dist < -PLANE_DIST_TOLERANCE)
  359.                                         masks.centermask |= sidebit;
  360.        
  361.                                 masks.facemask |= facebit;
  362.                                 masks.sidemask |= sidebit;
  363.                         }
  364.  
  365.                         facebit <<= 2;
  366.                 }
  367.  
  368.         }
  369.         return masks;
  370. }
  371.  
  372. //this was converted from get_seg_masks()...it fills in an array of 6
  373. //elements for the distace behind each side, or zero if not behind
  374. //only gets centermask, and assumes zero rad
  375. static uint8_t get_side_dists(fvcvertptr &vcvertptr, const vms_vector &checkp, const shared_segment &segnum, std::array<fix, 6> &side_dists)
  376. {
  377.         int                     sn,facebit,sidebit;
  378.         ubyte                   mask;
  379.         auto &sides = segnum.sides;
  380.  
  381.         //check point against each side of segment. return bitmask
  382.  
  383.         mask = 0;
  384.  
  385.         side_dists = {};
  386.         for (sn=0,facebit=sidebit=1;sn<6;sn++,sidebit<<=1) {
  387.                 auto &s = sides[sn];
  388.  
  389.                 // Get number of faces on this side, and at vertex_list, store vertices.
  390.                 //      If one face, then vertex_list indicates a quadrilateral.
  391.                 //      If two faces, then 0,1,2 define one triangle, 3,4,5 define the second.
  392.                 const auto v = create_abs_vertex_lists(segnum, s, sn);
  393.                 const auto &num_faces = v.first;
  394.                 const auto &vertex_list = v.second;
  395.  
  396.                 //ok...this is important.  If a side has 2 faces, we need to know if
  397.                 //those faces form a concave or convex side.  If the side pokes out,
  398.                 //then a point is on the back of the side if it is behind BOTH faces,
  399.                 //but if the side pokes in, a point is on the back if behind EITHER face.
  400.  
  401.                 if (num_faces==2) {
  402.                         int     center_count;
  403.  
  404.                         const auto vertnum = min(vertex_list[0],vertex_list[2]);
  405.                         auto &mvert = *vcvertptr(vertnum);
  406.  
  407.                         auto a = vertex_list[4] < vertex_list[1]
  408.                                 ? std::make_pair(vertex_list[4], &s.normals[0])
  409.                                 : std::make_pair(vertex_list[1], &s.normals[1]);
  410.                         const auto mdist = vm_dist_to_plane(vcvertptr(a.first), *a.second, mvert);
  411.  
  412.                         center_count = 0;
  413.  
  414.                         for (int fn=0;fn<2;fn++,facebit<<=1) {
  415.  
  416.                                 const auto dist = vm_dist_to_plane(checkp, s.normals[fn], mvert);
  417.  
  418.                                 if (dist < -PLANE_DIST_TOLERANCE) {     //in front of face
  419.                                         center_count++;
  420.                                         side_dists[sn] += dist;
  421.                                 }
  422.  
  423.                         }
  424.  
  425.                         if (!(mdist > PLANE_DIST_TOLERANCE)) {          //must be behind both faces
  426.  
  427.                                 if (center_count==2) {
  428.                                         mask |= sidebit;
  429.                                         side_dists[sn] /= 2;            //get average
  430.                                 }
  431.                                        
  432.  
  433.                         }
  434.                         else {                                                  //must be behind at least one face
  435.  
  436.                                 if (center_count) {
  437.                                         mask |= sidebit;
  438.                                         if (center_count==2)
  439.                                                 side_dists[sn] /= 2;            //get average
  440.  
  441.                                 }
  442.                         }
  443.  
  444.  
  445.                 }
  446.                 else {                          //only one face on this side
  447.                         //use lowest point number
  448.  
  449.                         auto b = begin(vertex_list);
  450.                         auto vertnum = *std::min_element(b, std::next(b, 4));
  451.  
  452.                         const auto dist = vm_dist_to_plane(checkp, s.normals[0], vcvertptr(vertnum));
  453.        
  454.                         if (dist < -PLANE_DIST_TOLERANCE) {
  455.                                 mask |= sidebit;
  456.                                 side_dists[sn] = dist;
  457.                         }
  458.        
  459.                         facebit <<= 2;
  460.                 }
  461.  
  462.         }
  463.  
  464.         return mask;
  465.  
  466. }
  467.  
  468. #ifndef NDEBUG
  469. //returns true if errors detected
  470. static int check_norms(const shared_segment &segp, const unsigned sidenum, const unsigned facenum, const shared_segment &csegp, const unsigned csidenum, const unsigned cfacenum)
  471. {
  472.         const auto &n0 = segp.sides[sidenum].normals[facenum];
  473.         const auto &n1 = csegp.sides[csidenum].normals[cfacenum];
  474.         if (n0.x != -n1.x || n0.y != -n1.y || n0.z != -n1.z)
  475.                 return 1;
  476.         else
  477.                 return 0;
  478. }
  479.  
  480. static void invert_shared_side_triangle_type(shared_side &s)
  481. {
  482.         const auto t = s.get_type();
  483.         side_type nt;
  484.         switch (t)
  485.         {
  486.                 case side_type::tri_02:
  487.                         nt = side_type::tri_13;
  488.                         break;
  489.                 case side_type::tri_13:
  490.                         nt = side_type::tri_02;
  491.                         break;
  492.                 default:
  493.                         return;
  494.         }
  495.         s.set_type(nt);
  496. }
  497. #endif
  498.  
  499. //heavy-duty error checking
  500. int check_segment_connections(void)
  501. {
  502.         int errors=0;
  503.  
  504.         range_for (const auto &&seg, vmsegptridx)
  505.         {
  506.                 range_for (const int sidenum, xrange(6u)) {
  507. #ifndef NDEBUG
  508.                         const auto v = create_abs_vertex_lists(seg, sidenum);
  509.                         const auto &num_faces = v.first;
  510.                         const auto &vertex_list = v.second;
  511. #endif
  512.                         auto csegnum = seg->children[sidenum];
  513.                         if (IS_CHILD(csegnum)) {
  514.                                 auto cseg = vcsegptr(csegnum);
  515.                                 auto csidenum = find_connect_side(seg,cseg);
  516.  
  517.                                 if (csidenum == side_none)
  518.                                 {
  519.                                         shared_segment &rseg = *seg;
  520.                                         const shared_segment &rcseg = *cseg;
  521.                                         const unsigned segi = seg.get_unchecked_index();
  522.                                         LevelError("Segment #%u side %u has asymmetric link to segment %u.  Coercing to segment_none; Segments[%u].children={%hu, %hu, %hu, %hu, %hu, %hu}, Segments[%u].children={%hu, %hu, %hu, %hu, %hu, %hu}.", segi, sidenum, csegnum, segi, rseg.children[0], rseg.children[1], rseg.children[2], rseg.children[3], rseg.children[4], rseg.children[5], csegnum, rcseg.children[0], rcseg.children[1], rcseg.children[2], rcseg.children[3], rcseg.children[4], rcseg.children[5]);
  523.                                         rseg.children[sidenum] = segment_none;
  524.                                         errors = 1;
  525.                                         continue;
  526.                                 }
  527.  
  528. #ifndef NDEBUG
  529.                                 const auto cv = create_abs_vertex_lists(cseg, csidenum);
  530.                                 const auto &con_num_faces = cv.first;
  531.                                 const auto &con_vertex_list = cv.second;
  532.  
  533.                                 if (con_num_faces != num_faces) {
  534.                                         LevelError("Segment #%u side %u: wrong faces: con_num_faces=%" PRIuFAST32 " num_faces=%" PRIuFAST32 ".", seg.get_unchecked_index(), sidenum, con_num_faces, num_faces);
  535.                                         errors = 1;
  536.                                 }
  537.                                 else
  538.                                         if (num_faces == 1) {
  539.                                                 int t;
  540.  
  541.                                                 for (t=0;t<4 && con_vertex_list[t]!=vertex_list[0];t++);
  542.  
  543.                                                 if (t==4 ||
  544.                                                          vertex_list[0] != con_vertex_list[t] ||
  545.                                                          vertex_list[1] != con_vertex_list[(t+3)%4] ||
  546.                                                          vertex_list[2] != con_vertex_list[(t+2)%4] ||
  547.                                                          vertex_list[3] != con_vertex_list[(t+1)%4]) {
  548.                                                         LevelError("Segment #%u side %u: bad vertices.", seg.get_unchecked_index(), sidenum);
  549.                                                         errors = 1;
  550.                                                 }
  551.                                                 else
  552.                                                         errors |= check_norms(seg,sidenum,0,cseg,csidenum,0);
  553.        
  554.                                         }
  555.                                         else {
  556.        
  557.                                                 if (vertex_list[1] == con_vertex_list[1]) {
  558.                
  559.                                                         if (vertex_list[4] != con_vertex_list[4] ||
  560.                                                                  vertex_list[0] != con_vertex_list[2] ||
  561.                                                                  vertex_list[2] != con_vertex_list[0] ||
  562.                                                                  vertex_list[3] != con_vertex_list[5] ||
  563.                                                                  vertex_list[5] != con_vertex_list[3]) {
  564.                                                                 auto &cside = vmsegptr(csegnum)->shared_segment::sides[csidenum];
  565.                                                                 invert_shared_side_triangle_type(cside);
  566.                                                         } else {
  567.                                                                 errors |= check_norms(seg,sidenum,0,cseg,csidenum,0);
  568.                                                                 errors |= check_norms(seg,sidenum,1,cseg,csidenum,1);
  569.                                                         }
  570.        
  571.                                                 } else {
  572.                
  573.                                                         if (vertex_list[1] != con_vertex_list[4] ||
  574.                                                                  vertex_list[4] != con_vertex_list[1] ||
  575.                                                                  vertex_list[0] != con_vertex_list[5] ||
  576.                                                                  vertex_list[5] != con_vertex_list[0] ||
  577.                                                                  vertex_list[2] != con_vertex_list[3] ||
  578.                                                                  vertex_list[3] != con_vertex_list[2]) {
  579.                                                                 auto &cside = vmsegptr(csegnum)->shared_segment::sides[csidenum];
  580.                                                                 invert_shared_side_triangle_type(cside);
  581.                                                         } else {
  582.                                                                 errors |= check_norms(seg,sidenum,0,cseg,csidenum,1);
  583.                                                                 errors |= check_norms(seg,sidenum,1,cseg,csidenum,0);
  584.                                                         }
  585.                                                 }
  586.                                         }
  587. #endif
  588.                         }
  589.                 }
  590.         }
  591.         return errors;
  592. }
  593.  
  594. // Used to become a constant based on editor, but I wanted to be able to set
  595. // this for omega blob find_point_seg calls.
  596. // Would be better to pass a paremeter to the routine...--MK, 01/17/96
  597. #if defined(DXX_BUILD_DESCENT_II) || DXX_USE_EDITOR
  598. int     Doing_lighting_hack_flag=0;
  599. #else
  600. #define Doing_lighting_hack_flag 0
  601. #endif
  602.  
  603. // figure out what seg the given point is in, tracing through segments
  604. // returns segment number, or -1 if can't find segment
  605. static icsegptridx_t trace_segs(const d_level_shared_segment_state &LevelSharedSegmentState, const vms_vector &p0, const vcsegptridx_t oldsegnum, const unsigned recursion_count, visited_segment_bitarray_t &visited)
  606. {
  607.         int centermask;
  608.         std::array<fix, 6> side_dists;
  609.         fix biggest_val;
  610.         int sidenum, bit, biggest_side;
  611.         if (recursion_count >= LevelSharedSegmentState.Num_segments) {
  612.                 con_puts(CON_DEBUG, "trace_segs: Segment not found");
  613.                 return segment_none;
  614.         }
  615.         if (auto &&vs = visited[oldsegnum])
  616.                 return segment_none;
  617.         else
  618.                 vs = true;
  619.  
  620.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  621.         auto &Vertices = LevelSharedVertexState.get_vertices();
  622.         centermask = get_side_dists(Vertices.vcptr, p0, oldsegnum, side_dists);         //check old segment
  623.         if (centermask == 0) // we are in the old segment
  624.                 return oldsegnum; //..say so
  625.  
  626.         for (;;) {
  627.                 auto seg = oldsegnum;
  628.                 biggest_side = -1;
  629.                 biggest_val = 0;
  630.                 for (sidenum = 0, bit = 1; sidenum < 6; sidenum++, bit <<= 1)
  631.                 {
  632.                         if ((centermask & bit) && IS_CHILD(seg->children[sidenum])
  633.                             && side_dists[sidenum] < biggest_val) {
  634.                                 biggest_val = side_dists[sidenum];
  635.                                 biggest_side = sidenum;
  636.                         }
  637.                 }
  638.  
  639.                 if (biggest_side == -1)
  640.                         break;
  641.  
  642.                 side_dists[biggest_side] = 0;
  643.                 // trace into adjacent segment:
  644.                 const auto &&check = trace_segs(LevelSharedSegmentState, p0, oldsegnum.absolute_sibling(seg->children[biggest_side]), recursion_count + 1, visited);
  645.                 if (check != segment_none)              //we've found a segment
  646.                         return check;
  647.         }
  648.         return segment_none;            //we haven't found a segment
  649. }
  650.  
  651. imsegptridx_t find_point_seg(const d_level_shared_segment_state &LevelSharedSegmentState, d_level_unique_segment_state &, const vms_vector &p, const imsegptridx_t segnum)
  652. {
  653.         return segnum.rebind_policy(find_point_seg(LevelSharedSegmentState, p, segnum));
  654. }
  655.  
  656. //Tries to find a segment for a point, in the following way:
  657. // 1. Check the given segment
  658. // 2. Recursively trace through attached segments
  659. // 3. Check all the segments
  660. //Returns segnum if found, or -1
  661. icsegptridx_t find_point_seg(const d_level_shared_segment_state &LevelSharedSegmentState, const vms_vector &p, const icsegptridx_t segnum)
  662. {
  663.         //allow segnum==-1, meaning we have no idea what segment point is in
  664.         if (segnum != segment_none) {
  665.                 visited_segment_bitarray_t visited;
  666.                 const auto &&newseg = trace_segs(LevelSharedSegmentState, p, segnum, 0, visited);
  667.                 if (newseg != segment_none)                     //we found a segment!
  668.                         return newseg;
  669.         }
  670.  
  671.         //couldn't find via attached segs, so search all segs
  672.  
  673.         //      MK: 10/15/94
  674.         //      This Doing_lighting_hack_flag thing added by mk because the hundreds of scrolling messages were
  675.         //      slowing down lighting, and in about 98% of cases, it would just return -1 anyway.
  676.         //      Matt: This really should be fixed, though.  We're probably screwing up our lighting in a few places.
  677.         if (!Doing_lighting_hack_flag) {
  678.                 auto &Segments = LevelSharedSegmentState.get_segments();
  679.                 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  680.                 auto &Vertices = LevelSharedVertexState.get_vertices();
  681.                 range_for (const auto &&segp, Segments.vmptridx)
  682.                 {
  683.                         if (get_seg_masks(Vertices.vcptr, p, segp, 0).centermask == 0)
  684.                                 return segp;
  685.                 }
  686.         }
  687.         return segment_none;
  688. }
  689.  
  690.  
  691. //--repair-- // ------------------------------------------------------------------------------
  692. //--repair-- void clsd_repair_center(int segnum)
  693. //--repair-- {
  694. //--repair--    int     sidenum;
  695. //--repair--
  696. //--repair--    //      --- Set repair center bit for all repair center segments.
  697. //--repair--    if (Segments[segnum].special == SEGMENT_IS_REPAIRCEN) {
  698. //--repair--            Lsegments[segnum].special_type |= SS_REPAIR_CENTER;
  699. //--repair--            Lsegments[segnum].special_segment = segnum;
  700. //--repair--    }
  701. //--repair--
  702. //--repair--    //      --- Set repair center bit for all segments adjacent to a repair center.
  703. //--repair--    for (sidenum=0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
  704. //--repair--            int     s = Segments[segnum].children[sidenum];
  705. //--repair--
  706. //--repair--            if ( (s != -1) && (Segments[s].special==SEGMENT_IS_REPAIRCEN) ) {
  707. //--repair--                    Lsegments[segnum].special_type |= SS_REPAIR_CENTER;
  708. //--repair--                    Lsegments[segnum].special_segment = s;
  709. //--repair--            }
  710. //--repair--    }
  711. //--repair-- }
  712.  
  713. //--repair-- // ------------------------------------------------------------------------------
  714. //--repair-- // --- Set destination points for all Materialization centers.
  715. //--repair-- void clsd_materialization_center(int segnum)
  716. //--repair-- {
  717. //--repair--    if (Segments[segnum].special == SEGMENT_IS_ROBOTMAKER) {
  718. //--repair--
  719. //--repair--    }
  720. //--repair-- }
  721. //--repair--
  722. //--repair-- int        Lsegment_highest_segment_index, Lsegment_highest_vertex_index;
  723. //--repair--
  724. //--repair-- // ------------------------------------------------------------------------------
  725. //--repair-- // Create data specific to mine which doesn't get written to disk.
  726. //--repair-- // Highest_segment_index and Highest_object_index must be valid.
  727. //--repair-- // 07/21:  set repair center bit
  728. //--repair-- void create_local_segment_data(void)
  729. //--repair-- {
  730. //--repair--    int     segnum;
  731. //--repair--
  732. //--repair--    //      --- Initialize all Lsegments.
  733. //--repair--    for (segnum=0; segnum <= Highest_segment_index; segnum++) {
  734. //--repair--            Lsegments[segnum].special_type = 0;
  735. //--repair--            Lsegments[segnum].special_segment = -1;
  736. //--repair--    }
  737. //--repair--
  738. //--repair--    for (segnum=0; segnum <= Highest_segment_index; segnum++) {
  739. //--repair--
  740. //--repair--            clsd_repair_center(segnum);
  741. //--repair--            clsd_materialization_center(segnum);
  742. //--repair--    
  743. //--repair--    }
  744. //--repair--
  745. //--repair--    //      Set check variables.
  746. //--repair--    //      In main game loop, make sure these are valid, else Lsegments is not valid.
  747. //--repair--    Lsegment_highest_segment_index = Highest_segment_index;
  748. //--repair--    Lsegment_highest_vertex_index = Highest_vertex_index;
  749. //--repair-- }
  750. //--repair--
  751. //--repair-- // ------------------------------------------------------------------------------------------
  752. //--repair-- // Sort of makes sure create_local_segment_data has been called for the currently executing mine.
  753. //--repair-- // It is not failsafe, as you will see if you look at the code.
  754. //--repair-- // Returns 1 if Lsegments appears valid, 0 if not.
  755. //--repair-- int check_lsegments_validity(void)
  756. //--repair-- {
  757. //--repair--    return ((Lsegment_highest_segment_index == Highest_segment_index) && (Lsegment_highest_vertex_index == Highest_vertex_index));
  758. //--repair-- }
  759.  
  760. }
  761.  
  762. #define MAX_LOC_POINT_SEGS      64
  763.  
  764. namespace dsx {
  765. #if defined(DXX_BUILD_DESCENT_I)
  766. static inline void add_to_fcd_cache(int seg0, int seg1, int depth, vm_distance dist)
  767. {
  768.         (void)(seg0||seg1||depth||dist);
  769. }
  770. #elif defined(DXX_BUILD_DESCENT_II)
  771. #define MIN_CACHE_FCD_DIST      (F1_0*80)       //      Must be this far apart for cache lookup to succeed.  Recognizes small changes in distance matter at small distances.
  772. #define MAX_FCD_CACHE   8
  773.  
  774. namespace {
  775.  
  776. struct fcd_data {
  777.         segnum_t        seg0, seg1;
  778.         int csd;
  779.         vm_distance dist;
  780. };
  781.  
  782. }
  783.  
  784. int     Fcd_index = 0;
  785. static std::array<fcd_data, MAX_FCD_CACHE> Fcd_cache;
  786. fix64   Last_fcd_flush_time;
  787.  
  788. //      ----------------------------------------------------------------------------------------------------------
  789. void flush_fcd_cache(void)
  790. {
  791.         Fcd_index = 0;
  792.  
  793.         range_for (auto &i, Fcd_cache)
  794.                 i.seg0 = segment_none;
  795. }
  796.  
  797. //      ----------------------------------------------------------------------------------------------------------
  798. static void add_to_fcd_cache(int seg0, int seg1, int depth, vm_distance dist)
  799. {
  800.         if (dist > MIN_CACHE_FCD_DIST) {
  801.                 Fcd_cache[Fcd_index].seg0 = seg0;
  802.                 Fcd_cache[Fcd_index].seg1 = seg1;
  803.                 Fcd_cache[Fcd_index].csd = depth;
  804.                 Fcd_cache[Fcd_index].dist = dist;
  805.  
  806.                 Fcd_index++;
  807.  
  808.                 if (Fcd_index >= MAX_FCD_CACHE)
  809.                         Fcd_index = 0;
  810.         } else {
  811.                 //      If it's in the cache, remove it.
  812.                 range_for (auto &i, Fcd_cache)
  813.                         if (i.seg0 == seg0)
  814.                                 if (i.seg1 == seg1) {
  815.                                         Fcd_cache[Fcd_index].seg0 = segment_none;
  816.                                         break;
  817.                                 }
  818.         }
  819.  
  820. }
  821. #endif
  822.  
  823. //      ----------------------------------------------------------------------------------------------------------
  824. //      Determine whether seg0 and seg1 are reachable in a way that allows sound to pass.
  825. //      Search up to a maximum depth of max_depth.
  826. //      Return the distance.
  827. vm_distance find_connected_distance(const vms_vector &p0, const vcsegptridx_t seg0, const vms_vector &p1, const vcsegptridx_t seg1, int max_depth, WALL_IS_DOORWAY_mask_t wid_flag)
  828. {
  829.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  830.         auto &Vertices = LevelSharedVertexState.get_vertices();
  831.         segnum_t                cur_seg;
  832.         int             qtail = 0, qhead = 0;
  833.         seg_seg seg_queue[MAX_SEGMENTS];
  834.         short           depth[MAX_SEGMENTS];
  835.         int             cur_depth;
  836.         int             num_points;
  837.         point_seg       point_segs[MAX_LOC_POINT_SEGS];
  838.  
  839.         //      If > this, will overrun point_segs buffer
  840. #ifdef WINDOWS
  841.         if (max_depth == -1) max_depth = 200;
  842. #endif 
  843.  
  844.         if (max_depth > MAX_LOC_POINT_SEGS-2) {
  845.                 max_depth = MAX_LOC_POINT_SEGS-2;
  846.         }
  847.  
  848.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  849.         auto &vcwallptr = Walls.vcptr;
  850.         if (seg0 == seg1) {
  851.                 return vm_vec_dist_quick(p0, p1);
  852.         } else {
  853.                 auto conn_side = find_connect_side(seg0, seg1);
  854.                 if (conn_side != side_none)
  855.                 {
  856. #if defined(DXX_BUILD_DESCENT_II)
  857.                         if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg1, conn_side) & wid_flag)
  858. #endif
  859.                         {
  860.                                 return vm_vec_dist_quick(p0, p1);
  861.                         }
  862.                 }
  863.         }
  864.  
  865. #if defined(DXX_BUILD_DESCENT_II)
  866.         //      Periodically flush cache.
  867.         if ((GameTime64 - Last_fcd_flush_time > F1_0*2) || (GameTime64 < Last_fcd_flush_time)) {
  868.                 flush_fcd_cache();
  869.                 Last_fcd_flush_time = GameTime64;
  870.         }
  871.  
  872.         else
  873.         //      Can't quickly get distance, so see if in Fcd_cache.
  874.         range_for (auto &i, Fcd_cache)
  875.                 if (i.seg0 == seg0 && i.seg1 == seg1)
  876.                 {
  877.                         return i.dist;
  878.                 }
  879. #endif
  880.  
  881.         num_points = 0;
  882.  
  883.         visited_segment_bitarray_t visited;
  884.         memset(depth, 0, sizeof(depth[0]) * (Highest_segment_index+1));
  885.  
  886.         cur_seg = seg0;
  887.         visited[cur_seg] = true;
  888.         cur_depth = 0;
  889.  
  890.         while (cur_seg != seg1) {
  891.                 const cscusegment segp = *vmsegptr(cur_seg);
  892.                 for (int sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
  893.  
  894.                         int     snum = sidenum;
  895.  
  896.                         const auto this_seg = segp.s.children[snum];
  897.                         if (!IS_CHILD(this_seg))
  898.                                 continue;
  899.                         if (!wid_flag.value || (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, snum) & wid_flag))
  900.                         {
  901.                                 if (!visited[this_seg]) {
  902.                                         seg_queue[qtail].start = cur_seg;
  903.                                         seg_queue[qtail].end = this_seg;
  904.                                         visited[this_seg] = true;
  905.                                         depth[qtail++] = cur_depth+1;
  906.                                         if (max_depth != -1) {
  907.                                                 if (depth[qtail-1] == max_depth) {
  908.                                                         constexpr auto Connected_segment_distance = 1000;
  909.                                                         add_to_fcd_cache(seg0, seg1, Connected_segment_distance, fcd_abort_cache_value);
  910.                                                         return fcd_abort_return_value;
  911.                                                 }
  912.                                         } else if (this_seg == seg1) {
  913.                                                 goto fcd_done1;
  914.                                         }
  915.                                 }
  916.  
  917.                         }
  918.                 }       //      for (sidenum...
  919.  
  920.                 if (qhead >= qtail) {
  921.                         constexpr auto Connected_segment_distance = 1000;
  922.                         add_to_fcd_cache(seg0, seg1, Connected_segment_distance, fcd_abort_cache_value);
  923.                         return fcd_abort_return_value;
  924.                 }
  925.  
  926.                 cur_seg = seg_queue[qhead].end;
  927.                 cur_depth = depth[qhead];
  928.                 qhead++;
  929.  
  930. fcd_done1: ;
  931.         }       //      while (cur_seg ...
  932.  
  933.         //      Set qtail to the segment which ends at the goal.
  934.         while (seg_queue[--qtail].end != seg1)
  935.                 if (qtail < 0) {
  936.                         constexpr auto Connected_segment_distance = 1000;
  937.                         add_to_fcd_cache(seg0, seg1, Connected_segment_distance, fcd_abort_cache_value);
  938.                         return fcd_abort_return_value;
  939.                 }
  940.  
  941.         auto &vcvertptr = Vertices.vcptr;
  942.         while (qtail >= 0) {
  943.                 segnum_t        parent_seg, this_seg;
  944.  
  945.                 this_seg = seg_queue[qtail].end;
  946.                 parent_seg = seg_queue[qtail].start;
  947.                 point_segs[num_points].segnum = this_seg;
  948.                 compute_segment_center(vcvertptr, point_segs[num_points].point, vcsegptr(this_seg));
  949.                 num_points++;
  950.  
  951.                 if (parent_seg == seg0)
  952.                         break;
  953.  
  954.                 while (seg_queue[--qtail].end != parent_seg)
  955.                         Assert(qtail >= 0);
  956.         }
  957.  
  958.         point_segs[num_points].segnum = seg0;
  959.         compute_segment_center(vcvertptr, point_segs[num_points].point,seg0);
  960.         num_points++;
  961.  
  962.         if (num_points == 1) {
  963.                 return vm_vec_dist_quick(p0, p1);
  964.         }
  965.         auto dist = vm_vec_dist_quick(p1, point_segs[1].point);
  966.                 dist += vm_vec_dist_quick(p0, point_segs[num_points-2].point);
  967.  
  968.                 for (int i=1; i<num_points-2; i++) {
  969.                         dist += vm_vec_dist_quick(point_segs[i].point, point_segs[i+1].point);
  970.                 }
  971.  
  972.         add_to_fcd_cache(seg0, seg1, num_points, dist);
  973.  
  974.         return dist;
  975.  
  976. }
  977.  
  978. }
  979.  
  980. namespace dcx {
  981.  
  982. static sbyte convert_to_byte(fix f)
  983. {
  984.         const uint8_t MATRIX_MAX = 0x7f;    // This is based on MATRIX_PRECISION, 9 => 0x7f
  985.         if (f >= 0x00010000)
  986.                 return MATRIX_MAX;
  987.         else if (f <= -0x00010000)
  988.                 return -MATRIX_MAX;
  989.         else
  990.                 return f >> MATRIX_PRECISION;
  991. }
  992.  
  993. }
  994.  
  995. #define VEL_PRECISION 12
  996.  
  997. namespace dsx {
  998.  
  999. //      Create a shortpos struct from an object.
  1000. //      Extract the matrix into byte values.
  1001. //      Create a position relative to vertex 0 with 1/256 normal "fix" precision.
  1002. //      Stuff segment in a short.
  1003. void create_shortpos_native(const d_level_shared_segment_state &LevelSharedSegmentState, shortpos &spp, const object_base &objp)
  1004. {
  1005.         auto &vcsegptr = LevelSharedSegmentState.get_segments().vcptr;
  1006.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1007.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1008.         auto &vcvertptr = Vertices.vcptr;
  1009.         spp.bytemat[0] = convert_to_byte(objp.orient.rvec.x);
  1010.         spp.bytemat[1] = convert_to_byte(objp.orient.uvec.x);
  1011.         spp.bytemat[2] = convert_to_byte(objp.orient.fvec.x);
  1012.         spp.bytemat[3] = convert_to_byte(objp.orient.rvec.y);
  1013.         spp.bytemat[4] = convert_to_byte(objp.orient.uvec.y);
  1014.         spp.bytemat[5] = convert_to_byte(objp.orient.fvec.y);
  1015.         spp.bytemat[6] = convert_to_byte(objp.orient.rvec.z);
  1016.         spp.bytemat[7] = convert_to_byte(objp.orient.uvec.z);
  1017.         spp.bytemat[8] = convert_to_byte(objp.orient.fvec.z);
  1018.  
  1019.         spp.segment = objp.segnum;
  1020.         const shared_segment &segp = *vcsegptr(objp.segnum);
  1021.         auto &vert = *vcvertptr(segp.verts[0]);
  1022.         spp.xo = (objp.pos.x - vert.x) >> RELPOS_PRECISION;
  1023.         spp.yo = (objp.pos.y - vert.y) >> RELPOS_PRECISION;
  1024.         spp.zo = (objp.pos.z - vert.z) >> RELPOS_PRECISION;
  1025.  
  1026.         spp.velx = (objp.mtype.phys_info.velocity.x) >> VEL_PRECISION;
  1027.         spp.vely = (objp.mtype.phys_info.velocity.y) >> VEL_PRECISION;
  1028.         spp.velz = (objp.mtype.phys_info.velocity.z) >> VEL_PRECISION;
  1029. }
  1030.  
  1031. void create_shortpos_little(const d_level_shared_segment_state &LevelSharedSegmentState, shortpos &spp, const object_base &objp)
  1032. {
  1033.         create_shortpos_native(LevelSharedSegmentState, spp, objp);
  1034. // swap the short values for the big-endian machines.
  1035.  
  1036.         if constexpr (words_bigendian)
  1037.         {
  1038.                 spp.xo = INTEL_SHORT(spp.xo);
  1039.                 spp.yo = INTEL_SHORT(spp.yo);
  1040.                 spp.zo = INTEL_SHORT(spp.zo);
  1041.                 spp.segment = INTEL_SHORT(spp.segment);
  1042.                 spp.velx = INTEL_SHORT(spp.velx);
  1043.                 spp.vely = INTEL_SHORT(spp.vely);
  1044.                 spp.velz = INTEL_SHORT(spp.velz);
  1045.         }
  1046. }
  1047.  
  1048. void extract_shortpos_little(const vmobjptridx_t objp, const shortpos *spp)
  1049. {
  1050.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1051.         auto &Objects = LevelUniqueObjectState.Objects;
  1052.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1053.         auto &vmobjptr = Objects.vmptr;
  1054.         auto sp = spp->bytemat.data();
  1055.  
  1056.         objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
  1057.         objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
  1058.         objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
  1059.         objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
  1060.         objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
  1061.         objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
  1062.         objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
  1063.         objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
  1064.         objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
  1065.  
  1066.         const segnum_t segnum = INTEL_SHORT(spp->segment);
  1067.  
  1068.         Assert(segnum <= Highest_segment_index);
  1069.  
  1070.         const auto &&segp = vmsegptridx(segnum);
  1071.         auto &vcvertptr = Vertices.vcptr;
  1072.         auto &vp = *vcvertptr(segp->verts[0]);
  1073.         objp->pos.x = (INTEL_SHORT(spp->xo) << RELPOS_PRECISION) + vp.x;
  1074.         objp->pos.y = (INTEL_SHORT(spp->yo) << RELPOS_PRECISION) + vp.y;
  1075.         objp->pos.z = (INTEL_SHORT(spp->zo) << RELPOS_PRECISION) + vp.z;
  1076.  
  1077.         objp->mtype.phys_info.velocity.x = (INTEL_SHORT(spp->velx) << VEL_PRECISION);
  1078.         objp->mtype.phys_info.velocity.y = (INTEL_SHORT(spp->vely) << VEL_PRECISION);
  1079.         objp->mtype.phys_info.velocity.z = (INTEL_SHORT(spp->velz) << VEL_PRECISION);
  1080.  
  1081.         obj_relink(vmobjptr, vmsegptr, objp, segp);
  1082. }
  1083.  
  1084. // create and extract quaternion structure from object data which greatly saves bytes by using quaternion instead or orientation matrix
  1085. void create_quaternionpos(quaternionpos &qpp, const object_base &objp)
  1086. {
  1087.         vms_quaternion_from_matrix(qpp.orient, objp.orient);
  1088.  
  1089.         qpp.pos = objp.pos;
  1090.         qpp.segment = objp.segnum;
  1091.         qpp.vel = objp.mtype.phys_info.velocity;
  1092.         qpp.rotvel = objp.mtype.phys_info.rotvel;
  1093. }
  1094.  
  1095. void extract_quaternionpos(const vmobjptridx_t objp, quaternionpos &qpp)
  1096. {
  1097.         auto &Objects = LevelUniqueObjectState.Objects;
  1098.         auto &vmobjptr = Objects.vmptr;
  1099.         vms_matrix_from_quaternion(objp->orient, qpp.orient);
  1100.  
  1101.         objp->pos = qpp.pos;
  1102.         objp->mtype.phys_info.velocity = qpp.vel;
  1103.         objp->mtype.phys_info.rotvel = qpp.rotvel;
  1104.        
  1105.         const auto segnum = qpp.segment;
  1106.         Assert(segnum <= Highest_segment_index);
  1107.         obj_relink(vmobjptr, vmsegptr, objp, vmsegptridx(segnum));
  1108. }
  1109.  
  1110.  
  1111. //      -----------------------------------------------------------------------------
  1112. //      Segment validation functions.
  1113. //      Moved from editor to game so we can compute surface normals at load time.
  1114. // -------------------------------------------------------------------------------
  1115.  
  1116. // ------------------------------------------------------------------------------------------
  1117. //      Extract a vector from a segment.  The vector goes from the start face to the end face.
  1118. //      The point on each face is the average of the four points forming the face.
  1119. static void extract_vector_from_segment(fvcvertptr &vcvertptr, const shared_segment &sp, vms_vector &vp, const uint_fast32_t istart, const uint_fast32_t iend)
  1120. {
  1121.         vp = {};
  1122.         auto &start = Side_to_verts[istart];
  1123.         auto &end = Side_to_verts[iend];
  1124.         auto &verts = sp.verts;
  1125.         range_for (const uint_fast32_t i, xrange(4u))
  1126.         {
  1127.                 vm_vec_sub2(vp, vcvertptr(verts[start[i]]));
  1128.                 vm_vec_add2(vp, vcvertptr(verts[end[i]]));
  1129.         }
  1130.         vm_vec_scale(vp,F1_0/4);
  1131. }
  1132.  
  1133. //create a matrix that describes the orientation of the given segment
  1134. void extract_orient_from_segment(fvcvertptr &vcvertptr, vms_matrix &m, const shared_segment &seg)
  1135. {
  1136.         vms_vector fvec,uvec;
  1137.  
  1138.         extract_vector_from_segment(vcvertptr, seg, fvec, WFRONT, WBACK);
  1139.         extract_vector_from_segment(vcvertptr, seg, uvec, WBOTTOM, WTOP);
  1140.  
  1141.         //vector to matrix does normalizations and orthogonalizations
  1142.         vm_vector_2_matrix(m, fvec, &uvec, nullptr);
  1143. }
  1144.  
  1145. #if !DXX_USE_EDITOR
  1146. namespace {
  1147. #endif
  1148.  
  1149. // ------------------------------------------------------------------------------------------
  1150. //      Extract the forward vector from segment *sp, return in *vp.
  1151. //      The forward vector is defined to be the vector from the the center of the front face of the segment
  1152. // to the center of the back face of the segment.
  1153. void extract_forward_vector_from_segment(fvcvertptr &vcvertptr, const shared_segment &sp, vms_vector &vp)
  1154. {
  1155.         extract_vector_from_segment(vcvertptr, sp, vp, WFRONT, WBACK);
  1156. }
  1157.  
  1158. // ------------------------------------------------------------------------------------------
  1159. //      Extract the right vector from segment *sp, return in *vp.
  1160. //      The forward vector is defined to be the vector from the the center of the left face of the segment
  1161. // to the center of the right face of the segment.
  1162. void extract_right_vector_from_segment(fvcvertptr &vcvertptr, const shared_segment &sp, vms_vector &vp)
  1163. {
  1164.         extract_vector_from_segment(vcvertptr, sp, vp, WLEFT, WRIGHT);
  1165. }
  1166.  
  1167. // ------------------------------------------------------------------------------------------
  1168. //      Extract the up vector from segment *sp, return in *vp.
  1169. //      The forward vector is defined to be the vector from the the center of the bottom face of the segment
  1170. // to the center of the top face of the segment.
  1171. void extract_up_vector_from_segment(fvcvertptr &vcvertptr, const shared_segment &sp, vms_vector &vp)
  1172. {
  1173.         extract_vector_from_segment(vcvertptr, sp, vp, WBOTTOM, WTOP);
  1174. }
  1175.  
  1176. #if !DXX_USE_EDITOR
  1177. }
  1178. #endif
  1179.  
  1180. //      ----
  1181. //      A side is determined to be degenerate if the cross products of 3 consecutive points does not point outward.
  1182. __attribute_warn_unused_result
  1183. static unsigned check_for_degenerate_side(fvcvertptr &vcvertptr, const shared_segment &sp, const unsigned sidenum)
  1184. {
  1185.         auto &vp = Side_to_verts[sidenum];
  1186.         vms_vector      vec1, vec2;
  1187.         fix                     dot;
  1188.         int                     degeneracy_flag = 0;
  1189.  
  1190.         const auto segc = compute_segment_center(vcvertptr, sp);
  1191.         const auto &&sidec = compute_center_point_on_side(vcvertptr, sp, sidenum);
  1192.         const auto vec_to_center = vm_vec_sub(segc, sidec);
  1193.  
  1194.         //vm_vec_sub(&vec1, &Vertices[sp->verts[vp[1]]], &Vertices[sp->verts[vp[0]]]);
  1195.         //vm_vec_sub(&vec2, &Vertices[sp->verts[vp[2]]], &Vertices[sp->verts[vp[1]]]);
  1196.         //vm_vec_normalize(&vec1);
  1197.         //vm_vec_normalize(&vec2);
  1198.         const auto vp1 = vp[1];
  1199.         const auto vp2 = vp[2];
  1200.         auto &vert1 = *vcvertptr(sp.verts[vp1]);
  1201.         auto &vert2 = *vcvertptr(sp.verts[vp2]);
  1202.         vm_vec_normalized_dir(vec1, vert1, vcvertptr(sp.verts[vp[0]]));
  1203.         vm_vec_normalized_dir(vec2, vert2, vert1);
  1204.         const auto cross0 = vm_vec_cross(vec1, vec2);
  1205.  
  1206.         dot = vm_vec_dot(vec_to_center, cross0);
  1207.         if (dot <= 0)
  1208.                 degeneracy_flag |= 1;
  1209.  
  1210.         //vm_vec_sub(&vec1, &Vertices[sp->verts[vp[2]]], &Vertices[sp->verts[vp[1]]]);
  1211.         //vm_vec_sub(&vec2, &Vertices[sp->verts[vp[3]]], &Vertices[sp->verts[vp[2]]]);
  1212.         //vm_vec_normalize(&vec1);
  1213.         //vm_vec_normalize(&vec2);
  1214.         vm_vec_normalized_dir(vec1, vert2, vert1);
  1215.         vm_vec_normalized_dir(vec2, vcvertptr(sp.verts[vp[3]]), vert2);
  1216.         const auto cross1 = vm_vec_cross(vec1, vec2);
  1217.  
  1218.         dot = vm_vec_dot(vec_to_center, cross1);
  1219.         if (dot <= 0)
  1220.                 degeneracy_flag |= 1;
  1221.  
  1222.         return degeneracy_flag;
  1223. }
  1224.  
  1225. //      ----
  1226. //      See if a segment has gotten turned inside out, or something.
  1227. //      If so, set global Degenerate_segment_found and return 1, else return 0.
  1228. static unsigned check_for_degenerate_segment(fvcvertptr &vcvertptr, const shared_segment &sp)
  1229. {
  1230.         vms_vector      fvec, rvec, uvec;
  1231.         fix                     dot;
  1232.         int                     degeneracy_flag = 0;                            // degeneracy flag for current segment
  1233.  
  1234.         extract_forward_vector_from_segment(vcvertptr, sp, fvec);
  1235.         extract_right_vector_from_segment(vcvertptr, sp, rvec);
  1236.         extract_up_vector_from_segment(vcvertptr, sp, uvec);
  1237.  
  1238.         vm_vec_normalize(fvec);
  1239.         vm_vec_normalize(rvec);
  1240.         vm_vec_normalize(uvec);
  1241.  
  1242.         const auto cross = vm_vec_cross(fvec, rvec);
  1243.         dot = vm_vec_dot(cross, uvec);
  1244.  
  1245.         if (dot > 0)
  1246.                 degeneracy_flag = 0;
  1247.         else {
  1248.                 degeneracy_flag = 1;
  1249.         }
  1250.  
  1251.         //      Now, see if degenerate because of any side.
  1252.         range_for (const uint_fast32_t i, xrange(MAX_SIDES_PER_SEGMENT))
  1253.                 degeneracy_flag |= check_for_degenerate_side(vcvertptr, sp, i);
  1254.  
  1255. #if DXX_USE_EDITOR
  1256.         Degenerate_segment_found |= degeneracy_flag;
  1257. #endif
  1258.  
  1259.         return degeneracy_flag;
  1260.  
  1261. }
  1262.  
  1263. static void add_side_as_quad(shared_side &sidep, const vms_vector &normal)
  1264. {
  1265.         sidep.set_type(side_type::quad);
  1266.         sidep.normals[0] = normal;
  1267.         sidep.normals[1] = normal;
  1268.         //      If there is a connection here, we only formed the faces for the purpose of determining segment boundaries,
  1269.         //      so don't generate polys, else they will get rendered.
  1270. //      if (sp->children[sidenum] != -1)
  1271. //              sidep->render_flag = 0;
  1272. //      else
  1273. //              sidep->render_flag = 1;
  1274. }
  1275.  
  1276. }
  1277.  
  1278. namespace dcx {
  1279.  
  1280. // -------------------------------------------------------------------------------
  1281. //      Return v0, v1, v2 = 3 vertices with smallest numbers.  If *negate_flag set, then negate normal after computation.
  1282. //      Note, you cannot just compute the normal by treating the points in the opposite direction as this introduces
  1283. //      small differences between normals which should merely be opposites of each other.
  1284. static void get_verts_for_normal(verts_for_normal &r, const unsigned va, const unsigned vb, const unsigned vc, const unsigned vd)
  1285. {
  1286.         auto &v = r.vsorted;
  1287.         std::array<unsigned, 4> w;
  1288.  
  1289.         //      w is a list that shows how things got scrambled so we know if our normal is pointing backwards
  1290.         range_for (const unsigned i, xrange(4u))
  1291.                 w[i] = i;
  1292.  
  1293.         v[0] = va;
  1294.         v[1] = vb;
  1295.         v[2] = vc;
  1296.         v[3] = vd;
  1297.  
  1298.         range_for (const unsigned i, xrange(1u, 4u))
  1299.                 range_for (const unsigned j, xrange(i))
  1300.                         if (v[j] > v[i]) {
  1301.                                 using std::swap;
  1302.                                 swap(v[j], v[i]);
  1303.                                 swap(w[j], w[i]);
  1304.                         }
  1305.  
  1306.         if (!((v[0] < v[1]) && (v[1] < v[2]) && (v[2] < v[3])))
  1307.                 LevelError("Level contains malformed geometry.");
  1308.  
  1309.         //      Now, if for any w[i] & w[i+1]: w[i+1] = (w[i]+3)%4, then must swap
  1310.         r.negate_flag = ((w[0] + 3) % 4) == w[1] || ((w[1] + 3) % 4) == w[2];
  1311. }
  1312.  
  1313. static void assign_side_normal(fvcvertptr &vcvertptr, vms_vector &n, const unsigned v0, const unsigned v1, const unsigned v2)
  1314. {
  1315.         verts_for_normal vfn;
  1316.         get_verts_for_normal(vfn, v0, v1, v2, UINT32_MAX);
  1317.         const auto &vsorted = vfn.vsorted;
  1318.         const auto &negate_flag = vfn.negate_flag;
  1319.         vm_vec_normal(n, vcvertptr(vsorted[0]), vcvertptr(vsorted[1]), vcvertptr(vsorted[2]));
  1320.         if (negate_flag)
  1321.                 vm_vec_negate(n);
  1322. }
  1323.  
  1324. }
  1325.  
  1326. namespace dsx {
  1327.  
  1328. // -------------------------------------------------------------------------------
  1329. static void add_side_as_2_triangles(fvcvertptr &vcvertptr, shared_segment &sp, const unsigned sidenum)
  1330. {
  1331.         auto &vs = Side_to_verts[sidenum];
  1332.         fix                     dot;
  1333.  
  1334.         const auto sidep = &sp.sides[sidenum];
  1335.  
  1336.         //      Choose how to triangulate.
  1337.         //      If a wall, then
  1338.         //              Always triangulate so segment is convex.
  1339.         //              Use Matt's formula: Na . AD > 0, where ABCD are vertices on side, a is face formed by A,B,C, Na is normal from face a.
  1340.         //      If not a wall, then triangulate so whatever is on the other side is triangulated the same (ie, between the same absoluate vertices)
  1341.         if (!IS_CHILD(sp.children[sidenum]))
  1342.         {
  1343.                 auto &verts = sp.verts;
  1344.                 auto &vvs0 = *vcvertptr(verts[vs[0]]);
  1345.                 auto &vvs1 = *vcvertptr(verts[vs[1]]);
  1346.                 auto &vvs2 = *vcvertptr(verts[vs[2]]);
  1347.                 auto &vvs3 = *vcvertptr(verts[vs[3]]);
  1348.                 const auto &&norm = vm_vec_normal(vvs0, vvs1, vvs2);
  1349.                 const auto &&vec_13 =   vm_vec_sub(vvs3, vvs1); //      vector from vertex 1 to vertex 3
  1350.                 dot = vm_vec_dot(norm, vec_13);
  1351.  
  1352.                 const vertex *n0v3, *n1v1;
  1353.                 //      Now, signifiy whether to triangulate from 0:2 or 1:3
  1354.                 sidep->set_type(dot >= 0 ? (n0v3 = &vvs2, n1v1 = &vvs0, side_type::tri_02) : (n0v3 = &vvs3, n1v1 = &vvs1, side_type::tri_13));
  1355.  
  1356.                 //      Now, based on triangulation type, set the normals.
  1357.                 vm_vec_normal(sidep->normals[0], vvs0, vvs1, *n0v3);
  1358.                 vm_vec_normal(sidep->normals[1], *n1v1, vvs2, vvs3);
  1359.         } else {
  1360.                 std::array<unsigned, 4> v;
  1361.  
  1362.                 range_for (const unsigned i, xrange(4u))
  1363.                         v[i] = sp.verts[vs[i]];
  1364.  
  1365.                 verts_for_normal vfn;
  1366.                 get_verts_for_normal(vfn, v[0], v[1], v[2], v[3]);
  1367.                 auto &vsorted = vfn.vsorted;
  1368.  
  1369.                 unsigned s0v2, s1v0;
  1370.                 if ((vsorted[0] == v[0]) || (vsorted[0] == v[2])) {
  1371.                         sidep->set_type(side_type::tri_02);
  1372.                         //      Now, get vertices for normal for each triangle based on triangulation type.
  1373.                         s0v2 = v[2];
  1374.                         s1v0 = v[0];
  1375.                 } else {
  1376.                         sidep->set_type(side_type::tri_13);
  1377.                         //      Now, get vertices for normal for each triangle based on triangulation type.
  1378.                         s0v2 = v[3];
  1379.                         s1v0 = v[1];
  1380.                 }
  1381.                 assign_side_normal(vcvertptr, sidep->normals[0], v[0], v[1], s0v2);
  1382.                 assign_side_normal(vcvertptr, sidep->normals[1], s1v0, v[2], v[3]);
  1383.         }
  1384. }
  1385.  
  1386. }
  1387.  
  1388. namespace dcx {
  1389.  
  1390. static int sign(fix v)
  1391. {
  1392.  
  1393.         if (v > PLANE_DIST_TOLERANCE)
  1394.                 return 1;
  1395.         else if (v < -(PLANE_DIST_TOLERANCE+1))         //neg & pos round differently
  1396.                 return -1;
  1397.         else
  1398.                 return 0;
  1399. }
  1400.  
  1401. }
  1402.  
  1403. namespace dsx {
  1404.  
  1405. #if !DXX_USE_EDITOR
  1406. namespace {
  1407. #endif
  1408.  
  1409. // -------------------------------------------------------------------------------
  1410. void create_walls_on_side(fvcvertptr &vcvertptr, shared_segment &sp, const unsigned sidenum)
  1411. {
  1412.         auto &vs = Side_to_verts[sidenum];
  1413.         const auto v0 = sp.verts[vs[0]];
  1414.         const auto v1 = sp.verts[vs[1]];
  1415.         const auto v2 = sp.verts[vs[2]];
  1416.         const auto v3 = sp.verts[vs[3]];
  1417.  
  1418.         verts_for_normal vfn;
  1419.         get_verts_for_normal(vfn, v0, v1, v2, v3);
  1420.         auto &vm1 = vfn.vsorted[1];
  1421.         auto &vm2 = vfn.vsorted[2];
  1422.         auto &vm3 = vfn.vsorted[3];
  1423.         auto &negate_flag = vfn.negate_flag;
  1424.  
  1425.         auto &vvm0 = *vcvertptr(vfn.vsorted[0]);
  1426.         auto &&vn = vm_vec_normal(vvm0, vcvertptr(vm1), vcvertptr(vm2));
  1427.         const fix dist_to_plane = abs(vm_dist_to_plane(vcvertptr(vm3), vn, vvm0));
  1428.  
  1429.         if (negate_flag)
  1430.                 vm_vec_negate(vn);
  1431.  
  1432.         auto &s = sp.sides[sidenum];
  1433.         if (dist_to_plane > PLANE_DIST_TOLERANCE)
  1434.         {
  1435.                 add_side_as_2_triangles(vcvertptr, sp, sidenum);
  1436.  
  1437.                 //this code checks to see if we really should be triangulated, and
  1438.                 //de-triangulates if we shouldn't be.
  1439.  
  1440.                         int                     s0,s1;
  1441.  
  1442.                         const auto v = create_abs_vertex_lists(sp, s, sidenum);
  1443.                         const auto &vertex_list = v.second;
  1444.  
  1445.                         Assert(v.first == 2);
  1446.  
  1447.                         auto &vvn = *vcvertptr(min(vertex_list[0],vertex_list[2]));
  1448.  
  1449.                         const fix dist0 = vm_dist_to_plane(vcvertptr(vertex_list[1]), s.normals[1], vvn);
  1450.                         const fix dist1 = vm_dist_to_plane(vcvertptr(vertex_list[4]), s.normals[0], vvn);
  1451.  
  1452.                         s0 = sign(dist0);
  1453.                         s1 = sign(dist1);
  1454.  
  1455.                 if (!(s0 == 0 || s1 == 0 || s0 != s1))
  1456.                         return;
  1457.                 //detriangulate!
  1458.         }
  1459.         add_side_as_quad(s, vn);
  1460. }
  1461.  
  1462. // -------------------------------------------------------------------------------
  1463. //      Make a just-modified segment side valid.
  1464. void validate_segment_side(fvcvertptr &vcvertptr, const vmsegptridx_t sp, const unsigned sidenum)
  1465. {
  1466.         auto &sside = sp->shared_segment::sides[sidenum];
  1467.         auto &uside = sp->unique_segment::sides[sidenum];
  1468.         create_walls_on_side(vcvertptr, sp, sidenum);
  1469.         /*
  1470.          * If the texture was wrong, then fix it and log a diagnostic.  For
  1471.          * builtin missions, log the diagnostic at level CON_VERBOSE, since
  1472.          * retail levels trigger this during normal play.  For external
  1473.          * missions, log the diagnostic at level CON_URGENT.  External
  1474.          * levels might be fixable by contacting the author, but the retail
  1475.          * levels can only be fixed by using a Rebirth level patch file (not
  1476.          * supported yet).  When fixing the texture, change it to 0 for
  1477.          * walls and 1 for non-walls.  This should make walls transparent
  1478.          * for their primary texture; transparent non-walls usually generate
  1479.          * ugly visual artifacts, so choose a non-zero texture for them.
  1480.          *
  1481.          * Known affected retail levels (incomplete list):
  1482.  
  1483. Descent 2: Counterstrike
  1484. sha256: f1abf516512739c97b43e2e93611a2398fc9f8bc7a014095ebc2b6b2fd21b703  descent2.hog
  1485. Levels 1-3: clean
  1486.  
  1487. Level #4
  1488. segment #170 side #4 has invalid tmap 910 (NumTextures=910)
  1489. segment #171 side #5 has invalid tmap 910 (NumTextures=910)
  1490. segment #184 side #2 has invalid tmap 910 (NumTextures=910)
  1491. segment #188 side #5 has invalid tmap 910 (NumTextures=910)
  1492.  
  1493. Level #5
  1494. segment #141 side #4 has invalid tmap 910 (NumTextures=910)
  1495.  
  1496. Level #6
  1497. segment #128 side #4 has invalid tmap 910 (NumTextures=910)
  1498.  
  1499. Level #7
  1500. segment #26 side #5 has invalid tmap 910 (NumTextures=910)
  1501. segment #28 side #5 has invalid tmap 910 (NumTextures=910)
  1502. segment #60 side #5 has invalid tmap 910 (NumTextures=910)
  1503. segment #63 side #5 has invalid tmap 910 (NumTextures=910)
  1504. segment #161 side #4 has invalid tmap 910 (NumTextures=910)
  1505. segment #305 side #4 has invalid tmap 910 (NumTextures=910)
  1506. segment #427 side #4 has invalid tmap 910 (NumTextures=910)
  1507. segment #533 side #5 has invalid tmap 910 (NumTextures=910)
  1508. segment #536 side #4 has invalid tmap 910 (NumTextures=910)
  1509. segment #647 side #4 has invalid tmap 910 (NumTextures=910)
  1510. segment #648 side #5 has invalid tmap 910 (NumTextures=910)
  1511.  
  1512. Level #8
  1513. segment #0 side #4 has invalid tmap 910 (NumTextures=910)
  1514. segment #92 side #0 has invalid tmap 910 (NumTextures=910)
  1515. segment #92 side #5 has invalid tmap 910 (NumTextures=910)
  1516. segment #94 side #1 has invalid tmap 910 (NumTextures=910)
  1517. segment #94 side #2 has invalid tmap 910 (NumTextures=910)
  1518. segment #95 side #0 has invalid tmap 910 (NumTextures=910)
  1519. segment #95 side #1 has invalid tmap 910 (NumTextures=910)
  1520. segment #97 side #5 has invalid tmap 910 (NumTextures=910)
  1521. segment #98 side #3 has invalid tmap 910 (NumTextures=910)
  1522. segment #100 side #1 has invalid tmap 910 (NumTextures=910)
  1523. segment #102 side #1 has invalid tmap 910 (NumTextures=910)
  1524. segment #104 side #3 has invalid tmap 910 (NumTextures=910)
  1525.  
  1526. Levels 9-end: unchecked
  1527.  
  1528.          */
  1529.         const auto old_tmap_num = uside.tmap_num;
  1530.         if (old_tmap_num >= NumTextures)
  1531.                 uside.tmap_num = (
  1532.                         LevelErrorV(PLAYING_BUILTIN_MISSION ? CON_VERBOSE : CON_URGENT, "segment #%hu side #%i has invalid tmap %u (NumTextures=%u).", static_cast<segnum_t>(sp), sidenum, old_tmap_num, NumTextures),
  1533.                         (sside.wall_num == wall_none)
  1534.                 );
  1535.  
  1536.         //      Set render_flag.
  1537.         //      If side doesn't have a child, then render wall.  If it does have a child, but there is a temporary
  1538.         //      wall there, then do render wall.
  1539. //      if (sp->children[sidenum] == -1)
  1540. //              sp->sides[sidenum].render_flag = 1;
  1541. //      else if (sp->sides[sidenum].wall_num != -1)
  1542. //              sp->sides[sidenum].render_flag = 1;
  1543. //      else
  1544. //              sp->sides[sidenum].render_flag = 0;
  1545. }
  1546.  
  1547. // -------------------------------------------------------------------------------
  1548. //      Make a just-modified segment valid.
  1549. //              check all sides to see how many faces they each should have (0,1,2)
  1550. //              create new vector normals
  1551. void validate_segment(fvcvertptr &vcvertptr, const vmsegptridx_t sp)
  1552. {
  1553.         check_for_degenerate_segment(vcvertptr, sp);
  1554.  
  1555.         for (int side = 0; side < MAX_SIDES_PER_SEGMENT; side++)
  1556.                 validate_segment_side(vcvertptr, sp, side);
  1557. }
  1558.  
  1559. #if !DXX_USE_EDITOR
  1560. }
  1561. #endif
  1562.  
  1563. // -------------------------------------------------------------------------------
  1564. //      Validate all segments.
  1565. //      Highest_segment_index must be set.
  1566. //      For all used segments (number <= Highest_segment_index), segnum field must be != -1.
  1567. void validate_segment_all(d_level_shared_segment_state &LevelSharedSegmentState)
  1568. {
  1569.         auto &Segments = LevelSharedSegmentState.get_segments();
  1570.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1571.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1572.         range_for (const auto &&segp, Segments.vmptridx)
  1573.         {
  1574. #if DXX_USE_EDITOR
  1575.                 if (segp->segnum != segment_none)
  1576.                 #endif
  1577.                         validate_segment(Vertices.vcptr, segp);
  1578.         }
  1579.  
  1580. #if DXX_USE_EDITOR
  1581.         range_for (shared_segment &s, partial_range(Segments, Highest_segment_index + 1, Segments.size()))
  1582.                 s.segnum = segment_none;
  1583.         #endif
  1584. }
  1585.  
  1586.  
  1587. //      ------------------------------------------------------------------------------------------------------
  1588. //      Picks a random point in a segment like so:
  1589. //              From center, go up to 50% of way towards any of the 8 vertices.
  1590. void pick_random_point_in_seg(fvcvertptr &vcvertptr, vms_vector &new_pos, const shared_segment &sp)
  1591. {
  1592.         compute_segment_center(vcvertptr, new_pos, sp);
  1593.         const unsigned vnum = (d_rand() * MAX_VERTICES_PER_SEGMENT) >> 15;
  1594.         auto &&vec2 = vm_vec_sub(vcvertptr(sp.verts[vnum]), new_pos);
  1595.         vm_vec_scale(vec2, d_rand());          // d_rand() always in 0..1/2
  1596.         vm_vec_add2(new_pos, vec2);
  1597. }
  1598.  
  1599.  
  1600. //      ----------------------------------------------------------------------------------------------------------
  1601. //      Set the segment depth of all segments from start_seg in *segbuf.
  1602. //      Returns maximum depth value.
  1603. unsigned set_segment_depths(vcsegidx_t start_seg, const std::array<uint8_t, MAX_SEGMENTS> *const limit, segment_depth_array_t &depth)
  1604. {
  1605.         std::array<segnum_t, MAX_SEGMENTS> queue;
  1606.         int     head, tail;
  1607.  
  1608.         head = 0;
  1609.         tail = 0;
  1610.  
  1611.         visited_segment_bitarray_t visited;
  1612.  
  1613.         queue[tail++] = start_seg;
  1614.         visited[start_seg] = true;
  1615.         depth[start_seg] = 1;
  1616.  
  1617.         unsigned parent_depth;
  1618.         do {
  1619.                 const auto curseg = queue[head++];
  1620.                 parent_depth = depth[curseg];
  1621.  
  1622.                 range_for (const auto childnum, vcsegptr(curseg)->children)
  1623.                 {
  1624.                         if (childnum != segment_none && childnum != segment_exit)
  1625.                                 if (!limit || (*limit)[childnum])
  1626.                                 {
  1627.                                         auto &&v = visited[childnum];
  1628.                                         if (!v)
  1629.                                         {
  1630.                                                 v = true;
  1631.                                                 depth[childnum] = min(static_cast<unsigned>(std::numeric_limits<segment_depth_array_t::value_type>::max()), parent_depth + 1);
  1632.                                                 queue[tail++] = childnum;
  1633.                                         }
  1634.                                 }
  1635.                 }
  1636.         } while (head < tail);
  1637.  
  1638.         return parent_depth+1;
  1639. }
  1640.  
  1641. #if defined(DXX_BUILD_DESCENT_II)
  1642. //these constants should match the ones in seguvs
  1643. #define LIGHT_DISTANCE_THRESHOLD        (F1_0*80)
  1644. #define Magical_light_constant  (F1_0*16)
  1645.  
  1646. //      ------------------------------------------------------------------------------------------
  1647. //cast static light from a segment to nearby segments
  1648. static void apply_light_to_segment(visited_segment_bitarray_t &visited, const vmsegptridx_t segp, const vms_vector &segment_center, const fix light_intensity, const unsigned recursion_depth)
  1649. {
  1650.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1651.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1652.         fix                     dist_to_rseg;
  1653.         if (auto &&visited_ref = visited[segp])
  1654.         {
  1655.         }
  1656.         else
  1657.         {
  1658.                 visited_ref = true;
  1659.                 auto &vcvertptr = Vertices.vcptr;
  1660.                 const auto r_segment_center = compute_segment_center(vcvertptr, segp);
  1661.                 dist_to_rseg = vm_vec_dist_quick(r_segment_center, segment_center);
  1662.        
  1663.                 if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
  1664.                         fix     light_at_point;
  1665.                         if (dist_to_rseg > F1_0)
  1666.                                 light_at_point = fixdiv(Magical_light_constant, dist_to_rseg);
  1667.                         else
  1668.                                 light_at_point = Magical_light_constant;
  1669.        
  1670.                         if (light_at_point >= 0) {
  1671.                                 light_at_point = fixmul(light_at_point, light_intensity);
  1672. #if 0   // don't see the point, static_light can be greater than F1_0
  1673.                                 if (light_at_point >= F1_0)
  1674.                                         light_at_point = F1_0-1;
  1675.                                 if (light_at_point <= -F1_0)
  1676.                                         light_at_point = -(F1_0-1);
  1677. #endif
  1678.                                 segp->static_light += light_at_point;
  1679.                                 if (segp->static_light < 0)     // if it went negative, saturate
  1680.                                         segp->static_light = 0;
  1681.                         }       //      end if (light_at_point...
  1682.                 }       //      end if (dist_to_rseg...
  1683.         }
  1684.  
  1685.         if (recursion_depth < 2)
  1686.         {
  1687.                 auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1688.                 auto &vcwallptr = Walls.vcptr;
  1689.                 range_for (const int sidenum, xrange(6u)) {
  1690.                         if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, sidenum) & WID_RENDPAST_FLAG)
  1691.                                 apply_light_to_segment(visited, segp.absolute_sibling(segp->children[sidenum]), segment_center, light_intensity, recursion_depth+1);
  1692.                 }
  1693.         }
  1694. }
  1695.  
  1696.  
  1697. //update the static_light field in a segment, which is used for object lighting
  1698. //this code is copied from the editor routine calim_process_all_lights()
  1699. static void change_segment_light(const vmsegptridx_t segp, const unsigned sidenum, const unsigned dir)
  1700. {
  1701.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1702.         auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
  1703.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1704.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1705.         auto &vcwallptr = Walls.vcptr;
  1706.         if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, sidenum) & WID_RENDER_FLAG)
  1707.         {
  1708.                 auto &sidep = segp->unique_segment::sides[sidenum];
  1709.                 fix     light_intensity;
  1710.  
  1711.                 light_intensity = TmapInfo[sidep.tmap_num].lighting + TmapInfo[sidep.tmap_num2 & 0x3fff].lighting;
  1712.                 if (light_intensity) {
  1713.                         auto &vcvertptr = Vertices.vcptr;
  1714.                         const auto segment_center = compute_segment_center(vcvertptr, segp);
  1715.                         visited_segment_bitarray_t visited;
  1716.                         apply_light_to_segment(visited, segp, segment_center, light_intensity * dir, 0);
  1717.                 }
  1718.         }
  1719.  
  1720.         //this is a horrible hack to get around the horrible hack used to
  1721.         //smooth lighting values when an object moves between segments
  1722.         old_viewer = NULL;
  1723.  
  1724. }
  1725.  
  1726. //      ------------------------------------------------------------------------------------------
  1727. //      dir = +1 -> add light
  1728. //      dir = -1 -> subtract light
  1729. //      dir = 17 -> add 17x light
  1730. //      dir =  0 -> you are dumb
  1731. static void change_light(const d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, const vmsegptridx_t segnum, const uint8_t sidenum, const int dir)
  1732. {
  1733.         const fix ds = dir * DL_SCALE;
  1734.         auto &Dl_indices = LevelSharedDestructibleLightState.Dl_indices;
  1735.         const auto &&pr = cast_range_result<const dl_index &>(Dl_indices.vcptr);
  1736.         const auto &&er = std::equal_range(pr.begin(), pr.end(), dl_index{segnum, sidenum, 0, 0});
  1737.         auto &Delta_lights = LevelSharedDestructibleLightState.Delta_lights;
  1738.         range_for (auto &i, partial_range_t<const dl_index *>(er.first.base().base(), er.second.base().base()))
  1739.         {
  1740.                 const uint_fast32_t idx = i.index;
  1741.                         range_for (auto &j, partial_const_range(Delta_lights, idx, idx + i.count))
  1742.                         {
  1743.                                 assert(j.sidenum < MAX_SIDES_PER_SEGMENT);
  1744.                                 const auto &&segp = vmsegptr(j.segnum);
  1745.                                 auto &uvls = segp->unique_segment::sides[j.sidenum].uvls;
  1746.                                 range_for (const int k, xrange(4u)) {
  1747.                                         auto &l = uvls[k].l;
  1748.                                         const fix dl = ds * j.vert_light[k];
  1749.                                         if ((l += dl) < 0)
  1750.                                                 l = 0;
  1751.                                 }
  1752.                         }
  1753.         }
  1754.  
  1755.         //recompute static light for segment
  1756.         change_segment_light(segnum,sidenum,dir);
  1757. }
  1758.  
  1759. //      Subtract light cast by a light source from all surfaces to which it applies light.
  1760. //      This is precomputed data, stored at static light application time in the editor (the slow lighting function).
  1761. // returns 1 if lights actually subtracted, else 0
  1762. int subtract_light(const d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, const vmsegptridx_t segnum, const sidenum_fast_t sidenum)
  1763. {
  1764.         if (segnum->light_subtracted & (1 << sidenum)) {
  1765.                 return 0;
  1766.         }
  1767.  
  1768.         segnum->light_subtracted |= (1 << sidenum);
  1769.         change_light(LevelSharedDestructibleLightState, segnum, sidenum, -1);
  1770.         return 1;
  1771. }
  1772.  
  1773. //      Add light cast by a light source from all surfaces to which it applies light.
  1774. //      This is precomputed data, stored at static light application time in the editor (the slow lighting function).
  1775. //      You probably only want to call this after light has been subtracted.
  1776. // returns 1 if lights actually added, else 0
  1777. int add_light(const d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, const vmsegptridx_t segnum, sidenum_fast_t sidenum)
  1778. {
  1779.         if (!(segnum->light_subtracted & (1 << sidenum))) {
  1780.                 return 0;
  1781.         }
  1782.  
  1783.         segnum->light_subtracted &= ~(1 << sidenum);
  1784.         change_light(LevelSharedDestructibleLightState, segnum, sidenum, 1);
  1785.         return 1;
  1786. }
  1787.  
  1788. //      Parse the Light_subtracted array, turning on or off all lights.
  1789. void apply_all_changed_light(const d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, fvmsegptridx &vmsegptridx)
  1790. {
  1791.         range_for (const auto &&segp, vmsegptridx)
  1792.         {
  1793.                 for (int j=0; j<MAX_SIDES_PER_SEGMENT; j++)
  1794.                         if (segp->light_subtracted & (1 << j))
  1795.                                 change_light(LevelSharedDestructibleLightState, segp, j, -1);
  1796.         }
  1797. }
  1798.  
  1799. //      Should call this whenever a new mine gets loaded.
  1800. //      More specifically, should call this whenever something global happens
  1801. //      to change the status of static light in the mine.
  1802. void clear_light_subtracted(void)
  1803. {
  1804.         range_for (const auto &&segp, vmsegptr)
  1805.         {
  1806.                 segp->light_subtracted = 0;
  1807.         }
  1808. }
  1809.  
  1810. #define AMBIENT_SEGMENT_DEPTH           5
  1811.  
  1812. static void ambient_mark_bfs(const vmsegptridx_t segp, segment_lava_depth_array *segdepth_lava, segment_water_depth_array *segdepth_water, const unsigned depth, const uint_fast8_t s2f_bit)
  1813. {
  1814.         segp->s2_flags |= s2f_bit;
  1815.         if (segdepth_lava)
  1816.         {
  1817.                 auto &d = (*segdepth_lava)[segp];
  1818.                 if (d < depth)
  1819.                         d = depth;
  1820.                 else
  1821.                         segdepth_lava = nullptr;
  1822.         }
  1823.         if (segdepth_water)
  1824.         {
  1825.                 auto &d = (*segdepth_water)[segp];
  1826.                 if (d < depth)
  1827.                         d = depth;
  1828.                 else
  1829.                         segdepth_water = nullptr;
  1830.         }
  1831.         if (!segdepth_lava && !segdepth_water)
  1832.                 return;
  1833.  
  1834.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1835.         auto &vcwallptr = Walls.vcptr;
  1836.         for (unsigned i = 0; i < MAX_SIDES_PER_SEGMENT; ++i)
  1837.         {
  1838.                 const auto child = segp->children[i];
  1839.  
  1840.                 /*
  1841.                  * No explicit check for IS_CHILD.  If !IS_CHILD, then
  1842.                  * WALL_IS_DOORWAY never sets WID_RENDPAST_FLAG.
  1843.                  */
  1844.                 if (!(WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, i) & WID_RENDPAST_FLAG))
  1845.                         continue;
  1846.                 ambient_mark_bfs(segp.absolute_sibling(child), segdepth_lava, segdepth_water, depth - 1, s2f_bit);
  1847.         }
  1848. }
  1849.  
  1850. //      -----------------------------------------------------------------------------
  1851. //      Indicate all segments which are within audible range of falling water or lava,
  1852. //      and so should hear ambient gurgles.
  1853. void set_ambient_sound_flags()
  1854. {
  1855.         auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
  1856.         range_for (const auto &&segp, vmsegptr)
  1857.                 segp->s2_flags = 0;
  1858.         //      Now, all segments containing ambient lava or water sound makers are flagged.
  1859.         //      Additionally flag all segments which are within range of them.
  1860.         //      Mark all segments which are sources of the sound.
  1861.         segment_lava_depth_array segdepth_lava{};
  1862.         segment_water_depth_array segdepth_water{};
  1863.  
  1864.         range_for (const auto &&segp, vmsegptridx)
  1865.         {
  1866.                 for (unsigned j = 0; j < MAX_SIDES_PER_SEGMENT; ++j)
  1867.                 {
  1868.                         const auto &sside = segp->shared_segment::sides[j];
  1869.                         const auto &uside = segp->unique_segment::sides[j];
  1870.                         if (IS_CHILD(segp->children[j]) && sside.wall_num == wall_none)
  1871.                                 /* If this side is open and there is no wall defined,
  1872.                                  * then the texture is never visible to the player.
  1873.                                  * This happens normally in some level editors if the
  1874.                                  * texture is not cleared when the child segment is
  1875.                                  * added.  Skip this side.
  1876.                                  */
  1877.                                 continue;
  1878.                         const auto texture_flags = TmapInfo[uside.tmap_num].flags | TmapInfo[uside.tmap_num2 & 0x3fff].flags;
  1879.                         /* These variables do not need to be named, but naming them
  1880.                          * is the easiest way to establish sequence points, so that
  1881.                          * `sound_flag` is passed to `ambient_mark_bfs` only after
  1882.                          * both ternary expressions have finished.
  1883.                          */
  1884.                         uint8_t sound_flag = 0;
  1885.                         const auto pl = (texture_flags & TMI_VOLATILE) ? (sound_flag |= S2F_AMBIENT_LAVA, &segdepth_lava) : nullptr;
  1886.                         const auto pw = (texture_flags & TMI_WATER) ? (sound_flag |= S2F_AMBIENT_WATER, &segdepth_water) : nullptr;
  1887.                         if (sound_flag)
  1888.                                 ambient_mark_bfs(segp, pl, pw, AMBIENT_SEGMENT_DEPTH, sound_flag);
  1889.                 }
  1890.         }
  1891. }
  1892. #endif
  1893. }
  1894.