Subversion Repositories Games.Descent

Rev

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

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Interrogation functions for segment data structure.
  23.  *
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <math.h>
  29. #include <string.h>
  30. #include "key.h"
  31. #include "gr.h"
  32. #include "inferno.h"
  33. #include "segment.h"
  34. #include "editor.h"
  35. #include "editor/esegment.h"
  36. #include "dxxerror.h"
  37. #include "object.h"
  38. #include "gameseg.h"
  39. #include "render.h"
  40. #include "game.h"
  41. #include "wall.h"
  42. #include "switch.h"
  43. #include "fuelcen.h"
  44. #include "cntrlcen.h"
  45. #include "seguvs.h"
  46. #include "gameseq.h"
  47. #include "kdefs.h"
  48.  
  49. #include "medwall.h"
  50. #include "hostage.h"
  51.  
  52. #include "compiler-range_for.h"
  53. #include "d_range.h"
  54. #include "d_enumerate.h"
  55. #include "d_zip.h"
  56. #include "segiter.h"
  57.  
  58. int     Do_duplicate_vertex_check = 0;          // Gets set to 1 in med_create_duplicate_vertex, means to check for duplicate vertices in compress_mine
  59.  
  60. //      Remap all vertices in polygons in a segment through translation table xlate_verts.
  61. int ToggleBottom(void)
  62. {
  63.         Render_only_bottom = !Render_only_bottom;
  64.         Update_flags = UF_WORLD_CHANGED;
  65.         return 0;
  66. }
  67.  
  68. // -------------------------------------------------------------------------------
  69. //      Return number of times vertex vi appears in all segments.
  70. //      This function can be used to determine whether a vertex is used exactly once in
  71. //      all segments, in which case it can be freely moved because it is not connected
  72. //      to any other segment.
  73. static int med_vertex_count(int vi)
  74. {
  75.         int             count;
  76.  
  77.         count = 0;
  78.  
  79.         range_for (auto &s, Segments)
  80.         {
  81.                 auto sp = &s;
  82.                 if (sp->segnum != segment_none)
  83.                         range_for (auto &v, s.verts)
  84.                                 if (v == vi)
  85.                                         count++;
  86.         }
  87.  
  88.         return count;
  89. }
  90.  
  91. // -------------------------------------------------------------------------------
  92. int is_free_vertex(int vi)
  93. {
  94.         return med_vertex_count(vi) == 1;
  95. }
  96.  
  97. // -------------------------------------------------------------------------------
  98. //      Return true if one fixed point number is very close to another, else return false.
  99. static int fnear(fix f1, fix f2)
  100. {
  101.         return (abs(f1 - f2) <= FIX_EPSILON);
  102. }
  103.  
  104. // -------------------------------------------------------------------------------
  105. static int vnear(const vms_vector &vp1, const vms_vector &vp2)
  106. {
  107.         return fnear(vp1.x, vp2.x) && fnear(vp1.y, vp2.y) && fnear(vp1.z, vp2.z);
  108. }
  109.  
  110. // -------------------------------------------------------------------------------
  111. //      Add the vertex *vp to the global list of vertices, return its index.
  112. //      Search until a matching vertex is found (has nearly the same coordinates) or until Num_vertices
  113. // vertices have been looked at without a match.  If no match, add a new vertex.
  114. int med_add_vertex(const vertex &vp)
  115. {
  116.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  117.         int     count;                                  // number of used vertices found, for loops exits when count == Num_vertices
  118.  
  119. //      set_vertex_counts();
  120.  
  121.         const auto Num_vertices = LevelSharedVertexState.Num_vertices;
  122.         Assert(Num_vertices < MAX_SEGMENT_VERTICES);
  123.  
  124.         count = 0;
  125.         unsigned free_index = UINT32_MAX;
  126.         auto &Vertices = LevelSharedVertexState.get_vertices();
  127.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  128.         for (unsigned v = 0; v < MAX_SEGMENT_VERTICES && count < Num_vertices; ++v)
  129.                 if (Vertex_active[v]) {
  130.                         count++;
  131.                         if (vnear(vp, Vertices.vcptr(v))) {
  132.                                 return v;
  133.                         }
  134.                 } else if (free_index == UINT32_MAX)
  135.                         free_index = v;                                 // we want free_index to be the first free slot to add a vertex
  136.  
  137.         if (free_index == UINT32_MAX)
  138.                 free_index = Num_vertices;
  139.  
  140.         while (Vertex_active[free_index] && (free_index < MAX_VERTICES))
  141.                 free_index++;
  142.  
  143.         Assert(free_index < MAX_VERTICES);
  144.  
  145.         *Vertices.vmptr(free_index) = vp;
  146.         Vertex_active[free_index] = 1;
  147.  
  148.         ++LevelSharedVertexState.Num_vertices;
  149.  
  150.         if (Vertices.get_count() - 1 < free_index)
  151.                 Vertices.set_count(free_index + 1);
  152.  
  153.         return free_index;
  154. }
  155.  
  156. namespace dsx {
  157.  
  158. // ------------------------------------------------------------------------------------------
  159. //      Returns the index of a free segment.
  160. //      Scans the Segments array.
  161. segnum_t get_free_segment_number(segment_array &Segments)
  162. {
  163.         for (segnum_t segnum=0; segnum<MAX_SEGMENTS; segnum++)
  164.                 if (Segments[segnum].segnum == segment_none) {
  165.                         ++ LevelSharedSegmentState.Num_segments;
  166.                         if (segnum > Highest_segment_index)
  167.                                 Segments.set_count(segnum + 1);
  168.                         return segnum;
  169.                 }
  170.  
  171.         Assert(0);
  172.  
  173.         return 0;
  174. }
  175.  
  176. // -------------------------------------------------------------------------------
  177. //      Create a new segment, duplicating exactly, including vertex ids and children, the passed segment.
  178. segnum_t med_create_duplicate_segment(segment_array &Segments, const segment &sp)
  179. {
  180.         const auto segnum = get_free_segment_number(Segments);
  181.  
  182.         auto &nsp = *Segments.vmptr(segnum);
  183.         nsp = sp;
  184.         nsp.objects = object_none;
  185.  
  186.         return segnum;
  187. }
  188.  
  189. }
  190.  
  191. // -------------------------------------------------------------------------------
  192. //      Add the vertex *vp to the global list of vertices, return its index.
  193. //      This is the same as med_add_vertex, except that it does not search for the presence of the vertex.
  194. int med_create_duplicate_vertex(const vertex &vp)
  195. {
  196.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  197.         const auto Num_vertices = LevelSharedVertexState.Num_vertices;
  198.         Assert(Num_vertices < MAX_SEGMENT_VERTICES);
  199.  
  200.         Do_duplicate_vertex_check = 1;
  201.  
  202.         unsigned free_index = Num_vertices;
  203.  
  204.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  205.         while (Vertex_active[free_index] && (free_index < MAX_VERTICES))
  206.                 free_index++;
  207.  
  208.         Assert(free_index < MAX_VERTICES);
  209.  
  210.         auto &Vertices = LevelSharedVertexState.get_vertices();
  211.         *Vertices.vmptr(free_index) = vp;
  212.         Vertex_active[free_index] = 1;
  213.  
  214.         ++LevelSharedVertexState.Num_vertices;
  215.  
  216.         if (Vertices.get_count() - 1 < free_index)
  217.                 Vertices.set_count(free_index + 1);
  218.  
  219.         return free_index;
  220. }
  221.  
  222.  
  223. // -------------------------------------------------------------------------------
  224. //      Set the vertex *vp at index vnum in the global list of vertices, return its index (just for compatibility).
  225. int med_set_vertex(const unsigned vnum, const vertex &vp)
  226. {
  227.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  228.         auto &Vertices = LevelSharedVertexState.get_vertices();
  229.         *Vertices.vmptr(vnum) = vp;
  230.  
  231.         // Just in case this vertex wasn't active, mark it as active.
  232.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  233.         if (!Vertex_active[vnum]) {
  234.                 Vertex_active[vnum] = 1;
  235.                 ++LevelSharedVertexState.Num_vertices;
  236.                 if ((vnum > Vertices.get_count() - 1) && (vnum < NEW_SEGMENT_VERTICES)) {
  237.                         Vertices.set_count(vnum + 1);
  238.                 }
  239.         }
  240.  
  241.         return vnum;
  242. }
  243.  
  244. namespace dsx {
  245.  
  246. // -------------------------------------------------------------------------------
  247. void create_removable_wall(fvcvertptr &vcvertptr, const vmsegptridx_t sp, const unsigned sidenum, const unsigned tmap_num)
  248. {
  249.         create_walls_on_side(vcvertptr, sp, sidenum);
  250.  
  251.         sp->unique_segment::sides[sidenum].tmap_num = tmap_num;
  252.  
  253.         assign_default_uvs_to_side(sp, sidenum);
  254.         assign_light_to_side(sp, sidenum);
  255. }
  256.  
  257. #if 0
  258.  
  259. // ---------------------------------------------------------------------------------------------
  260. //      Orthogonalize matrix smat, returning result in rmat.
  261. //      Does not modify smat.
  262. //      Uses Gram-Schmidt process.
  263. //      See page 172 of Strang, Gilbert, Linear Algebra and its Applications
  264. //      Matt -- This routine should be moved to the vector matrix library.
  265. //      It IS legal for smat == rmat.
  266. //      We should also have the functions:
  267. //              mat_a = mat_b * scalar;                         // we now have mat_a = mat_a * scalar;
  268. //              mat_a = mat_b + mat_c * scalar; // or maybe not, maybe this is not primitive
  269. void make_orthogonal(vms_matrix *rmat,vms_matrix *smat)
  270. {
  271.         vms_matrix              tmat;
  272.         vms_vector              tvec1,tvec2;
  273.         float                           dot;
  274.  
  275.         // Copy source matrix to work area.
  276.         tmat = *smat;
  277.  
  278.         // Normalize the three rows of the matrix tmat.
  279.         vm_vec_normalize(&tmat.xrow);
  280.         vm_vec_normalize(&tmat.yrow);
  281.         vm_vec_normalize(&tmat.zrow);
  282.  
  283.         //      Now, compute the first vector.
  284.         // This is very easy -- just copy the (normalized) source vector.
  285.         rmat->zrow = tmat.zrow;
  286.  
  287.         // Now, compute the second vector.
  288.         // From page 172 of Strang, we use the equation:
  289.         //              b' = b - [transpose(q1) * b] * q1
  290.         //      where:  b  = the second row of tmat
  291.         //                              q1 = the first row of rmat
  292.         //                              b' = the second row of rmat
  293.  
  294.         // Compute: transpose(q1) * b
  295.         dot = vm_vec_dot(&rmat->zrow,&tmat.yrow);
  296.  
  297.         // Compute: b - dot * q1
  298.         rmat->yrow.x = tmat.yrow.x - fixmul(dot,rmat->zrow.x);
  299.         rmat->yrow.y = tmat.yrow.y - fixmul(dot,rmat->zrow.y);
  300.         rmat->yrow.z = tmat.yrow.z - fixmul(dot,rmat->zrow.z);
  301.  
  302.         // Now, compute the third vector.
  303.         // From page 173 of Strang, we use the equation:
  304.         //              c' = c - (q1*c)*q1 - (q2*c)*q2
  305.         //      where:  c  = the third row of tmat
  306.         //                              q1 = the first row of rmat
  307.         //                              q2 = the second row of rmat
  308.         //                              c' = the third row of rmat
  309.  
  310.         // Compute: q1*c
  311.         dot = vm_vec_dot(&rmat->zrow,&tmat.xrow);
  312.  
  313.         tvec1.x = fixmul(dot,rmat->zrow.x);
  314.         tvec1.y = fixmul(dot,rmat->zrow.y);
  315.         tvec1.z = fixmul(dot,rmat->zrow.z);
  316.  
  317.         // Compute: q2*c
  318.         dot = vm_vec_dot(&rmat->yrow,&tmat.xrow);
  319.  
  320.         tvec2.x = fixmul(dot,rmat->yrow.x);
  321.         tvec2.y = fixmul(dot,rmat->yrow.y);
  322.         tvec2.z = fixmul(dot,rmat->yrow.z);
  323.  
  324.         vm_vec_sub(&rmat->xrow,vm_vec_sub(&rmat->xrow,&tmat.xrow,&tvec1),&tvec2);
  325. }
  326.  
  327. #endif
  328.  
  329. // ------------------------------------------------------------------------------------------
  330. // Given a segment, extract the rotation matrix which defines it.
  331. // Do this by extracting the forward, right, up vectors and then making them orthogonal.
  332. // In the process of making the vectors orthogonal, favor them in the order forward, up, right.
  333. // This means that the forward vector will remain unchanged.
  334. void med_extract_matrix_from_segment(const shared_segment &sp, vms_matrix &rotmat)
  335. {
  336.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  337.         vms_vector      forwardvec,upvec;
  338.  
  339.         auto &Vertices = LevelSharedVertexState.get_vertices();
  340.         auto &vcvertptr = Vertices.vcptr;
  341.         extract_forward_vector_from_segment(vcvertptr, sp, forwardvec);
  342.         extract_up_vector_from_segment(vcvertptr, sp, upvec);
  343.  
  344.         if (((forwardvec.x == 0) && (forwardvec.y == 0) && (forwardvec.z == 0)) || ((upvec.x == 0) && (upvec.y == 0) && (upvec.z == 0))) {
  345.                 rotmat = vmd_identity_matrix;
  346.                 return;
  347.         }
  348.  
  349.  
  350.         vm_vector_2_matrix(rotmat, forwardvec, &upvec, nullptr);
  351.  
  352. #if 0
  353.         vms_matrix      rm;
  354.  
  355.         extract_forward_vector_from_segment(sp,&rm.zrow);
  356.         extract_right_vector_from_segment(sp,&rm.xrow);
  357.         extract_up_vector_from_segment(sp,&rm.yrow);
  358.  
  359.         vm_vec_normalize(&rm.xrow);
  360.         vm_vec_normalize(&rm.yrow);
  361.         vm_vec_normalize(&rm.zrow);
  362.  
  363.         make_orthogonal(rotmat,&rm);
  364.  
  365.         vm_vec_normalize(&rotmat->xrow);
  366.         vm_vec_normalize(&rotmat->yrow);
  367.         vm_vec_normalize(&rotmat->zrow);
  368.  
  369. // *rotmat = rm; // include this line (and remove the call to make_orthogonal) if you don't want the matrix orthogonalized
  370. #endif
  371. }
  372.  
  373. }
  374.  
  375. // ------------------------------------------------------------------------------------------
  376. //      Given a rotation matrix *rotmat which describes the orientation of a segment
  377. //      and a side destside, return the rotation matrix which describes the orientation for the side.
  378. void update_matrix_based_on_side(vms_matrix &rotmat,int destside)
  379. {
  380.         vms_angvec      rotvec;
  381.  
  382.         switch (destside) {
  383.                 case WLEFT:
  384.                         vm_angvec_make(&rotvec,0,0,-16384);
  385.                         rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
  386.                         break;
  387.  
  388.                 case WTOP:
  389.                         vm_angvec_make(&rotvec,-16384,0,0);
  390.                         rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
  391.                         break;
  392.  
  393.                 case WRIGHT:
  394.                         vm_angvec_make(&rotvec,0,0,16384);
  395.                         rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
  396.                         break;
  397.  
  398.                 case WBOTTOM:
  399.                         vm_angvec_make(&rotvec,+16384,-32768,0);        // bank was -32768, but I think that was an erroneous compensation
  400.                         rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
  401.                         break;
  402.  
  403.                 case WFRONT:
  404.                         vm_angvec_make(&rotvec,0,0,-32768);
  405.                         rotmat = vm_matrix_x_matrix(rotmat, vm_angles_2_matrix(rotvec));
  406.                         break;
  407.  
  408.                 case WBACK:
  409.                         break;
  410.         }
  411. }
  412.  
  413. //      -------------------------------------------------------------------------------------
  414. static void change_vertex_occurrences(int dest, int src)
  415. {
  416.         // Fix vertices in groups
  417.         range_for (auto &g, partial_range(GroupList, num_groups))
  418.                 g.vertices.replace(src, dest);
  419.  
  420.         // now scan all segments, changing occurrences of src to dest
  421.         range_for (const auto &&segp, vmsegptr)
  422.         {
  423.                 if (segp->segnum != segment_none)
  424.                         range_for (auto &v, segp->verts)
  425.                                 if (v == src)
  426.                                         v = dest;
  427.         }
  428. }
  429.  
  430. // --------------------------------------------------------------------------------------------------
  431. static void compress_vertices(void)
  432. {
  433.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  434.         const auto Num_vertices = LevelSharedVertexState.Num_vertices;
  435.         auto &Vertices = LevelSharedVertexState.get_vertices();
  436.         if (Vertices.get_count() == Num_vertices)
  437.                 return;
  438.  
  439.         unsigned vert = Vertices.get_count() - 1;       //MAX_SEGMENT_VERTICES-1;
  440.  
  441.         auto &vcvertptr = Vertices.vcptr;
  442.         auto &vmvertptr = Vertices.vmptr;
  443.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  444.         for (unsigned hole = 0; hole < vert; ++hole)
  445.                 if (!Vertex_active[hole]) {
  446.                         // found an unused vertex which is a hole if a used vertex follows (not necessarily immediately) it.
  447.                         for ( ; (vert>hole) && (!Vertex_active[vert]); vert--)
  448.                                 ;
  449.  
  450.                         if (vert > hole) {
  451.  
  452.                                 // Ok, hole is the index of a hole, vert is the index of a vertex which follows it.
  453.                                 // Copy vert into hole, update pointers to it.
  454.                                 *vmvertptr(hole) = *vcvertptr(vert);
  455.                                 change_vertex_occurrences(hole, vert);
  456.  
  457.                                 vert--;
  458.                         }
  459.                 }
  460.  
  461.         Vertices.set_count(Num_vertices);
  462. }
  463.  
  464. // --------------------------------------------------------------------------------------------------
  465. static void compress_segments(void)
  466. {
  467.         auto &Objects = LevelUniqueObjectState.Objects;
  468.         auto &vmobjptridx = Objects.vmptridx;
  469.         if (Highest_segment_index == LevelSharedSegmentState.Num_segments - 1)
  470.                 return;
  471.  
  472.         segnum_t                hole,seg;
  473.         seg = Highest_segment_index;
  474.  
  475.         auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
  476.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  477.         auto &vmwallptr = Walls.vmptr;
  478.         for (hole=0; hole < seg; hole++)
  479.                 if (Segments[hole].segnum == segment_none) {
  480.                         // found an unused segment which is a hole if a used segment follows (not necessarily immediately) it.
  481.                         for ( ; (seg>hole) && (Segments[seg].segnum == segment_none); seg--)
  482.                                 ;
  483.  
  484.                         if (seg > hole) {
  485.                                 // Ok, hole is the index of a hole, seg is the index of a segment which follows it.
  486.                                 // Copy seg into hole, update pointers to it, update Cursegp, Markedsegp if necessary.
  487.                                 Segments[hole] = Segments[seg];
  488.                                 Segments[seg].segnum = segment_none;
  489.  
  490.                                 if (Cursegp == &Segments[seg])
  491.                                         Cursegp = imsegptridx(hole);
  492.  
  493.                                 if (Markedsegp == &Segments[seg])
  494.                                         Markedsegp = imsegptridx(hole);
  495.  
  496.                                 // Fix segments in groups
  497.                                 range_for (auto &g, partial_range(GroupList, num_groups))
  498.                                         g.segments.replace(seg, hole);
  499.  
  500.                                 // Fix walls
  501.                                 range_for (const auto &&w, vmwallptr)
  502.                                         if (w->segnum == seg)
  503.                                                 w->segnum = hole;
  504.  
  505.                                 // Fix fuelcenters, robotcens, and triggers... added 2/1/95 -Yuan
  506.                                 range_for (auto &f, partial_range(LevelUniqueFuelcenterState.Station, LevelUniqueFuelcenterState.Num_fuelcenters))
  507.                                         if (f.segnum == seg)
  508.                                                 f.segnum = hole;
  509.  
  510.                                 range_for (auto &f, partial_range(RobotCenters, LevelSharedRobotcenterState.Num_robot_centers))
  511.                                         if (f.segnum == seg)
  512.                                                 f.segnum = hole;
  513.  
  514.                                 auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
  515.                                 auto &vmtrgptr = Triggers.vmptr;
  516.                                 range_for (const auto vt, vmtrgptr)
  517.                                 {
  518.                                         auto &t = *vt;
  519.                                         range_for (auto &l, partial_range(t.seg, t.num_links))
  520.                                                 if (l == seg)
  521.                                                         l = hole;
  522.                                 }
  523.  
  524.                                 auto &sp = *vmsegptr(hole);
  525.                                 range_for (auto &s, sp.children)
  526.                                 {
  527.                                         if (IS_CHILD(s)) {
  528.                                                 // Find out on what side the segment connection to the former seg is on in *csegp.
  529.                                                 range_for (auto &t, vmsegptr(s)->children)
  530.                                                 {
  531.                                                         if (t == seg) {
  532.                                                                 t = hole;                                       // It used to be connected to seg, so make it connected to hole
  533.                                                         }
  534.                                                 }       // end for t
  535.                                         }       // end if
  536.                                 }       // end for s
  537.  
  538.                                 //Update object segment pointers
  539.                                 range_for (const auto objp, objects_in(sp, vmobjptridx, vmsegptr))
  540.                                 {
  541.                                         Assert(objp->segnum == seg);
  542.                                         objp->segnum = hole;
  543.                                 }
  544.  
  545.                                 seg--;
  546.  
  547.                         }       // end if (seg > hole)
  548.                 }       // end if
  549.  
  550.         Segments.set_count(LevelSharedSegmentState.Num_segments);
  551.         med_create_new_segment_from_cursegp();
  552.  
  553. }
  554.  
  555.  
  556. // -------------------------------------------------------------------------------
  557. //      Combine duplicate vertices.
  558. //      If two vertices have the same coordinates, within some small tolerance, then assign
  559. //      the same vertex number to the two vertices, freeing up one of the vertices.
  560. void med_combine_duplicate_vertices(std::array<uint8_t, MAX_VERTICES> &vlp)
  561. {
  562.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  563.         auto &Vertices = LevelSharedVertexState.get_vertices();
  564.         auto &vcvertptridx = Vertices.vcptridx;
  565.         const auto &&range = make_range(vcvertptridx);
  566.         // Note: ok to do to <, rather than <= because w for loop starts at v+1
  567.         if (range.m_begin == range.m_end)
  568.                 return;
  569.         for (auto i = range.m_begin;;)
  570.         {
  571.                 const auto &&v = *i;
  572.                 if (++i == range.m_end)
  573.                         return;
  574.                 if (vlp[v]) {
  575.                         auto &vvp = *v;
  576.                         auto subrange = range;
  577.                         subrange.m_begin = i;
  578.                         range_for (auto &&w, subrange)
  579.                                 if (vlp[w]) {   //      used to be Vertex_active[w]
  580.                                         if (vnear(vvp, *w)) {
  581.                                                 change_vertex_occurrences(v, w);
  582.                                         }
  583.                                 }
  584.                 }
  585.         }
  586. }
  587.  
  588. // ------------------------------------------------------------------------------
  589. //      Compress mine at Segments and Vertices by squeezing out all holes.
  590. //      If no holes (ie, an unused segment followed by a used segment), then no action.
  591. //      If Cursegp or Markedsegp is a segment which gets moved to fill in a hole, then
  592. //      they are properly updated.
  593. void med_compress_mine(void)
  594. {
  595.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  596.         if (Do_duplicate_vertex_check) {
  597.                 auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  598.                 med_combine_duplicate_vertices(Vertex_active);
  599.                 Do_duplicate_vertex_check = 0;
  600.         }
  601.  
  602.         compress_segments();
  603.         compress_vertices();
  604.         set_vertex_counts();
  605.  
  606.         //--repair-- create_local_segment_data();
  607.  
  608.         //      This is necessary becuase a segment search (due to click in 3d window) uses the previous frame's
  609.         //      segment information, which could get changed by this.
  610.         Update_flags = UF_WORLD_CHANGED;
  611. }
  612.  
  613. namespace dsx {
  614.  
  615. // ------------------------------------------------------------------------------------------
  616. //      Copy texture map ids for each face in sseg to dseg.
  617. static void copy_tmap_ids(unique_segment &dseg, const unique_segment &sseg)
  618. {
  619.         range_for (const auto &&z, zip(sseg.sides, dseg.sides))
  620.         {
  621.                 auto &ds = std::get<1>(z);
  622.                 ds.tmap_num = std::get<0>(z).tmap_num;
  623.                 ds.tmap_num2 = 0;
  624.         }
  625. }
  626.  
  627. // ------------------------------------------------------------------------------------------
  628. //      Attach a segment with a rotated orientation.
  629. // Return value:
  630. //  0 = successful attach
  631. //  1 = No room in Segments[].
  632. //  2 = No room in Vertices[].
  633. //  3 = newside != WFRONT -- for now, the new segment must be attached at its (own) front side
  634. //       4 = already a face attached on destseg:destside
  635. static int med_attach_segment_rotated(const vmsegptridx_t destseg, const vmsegptr_t newseg, int destside, int newside,const vms_matrix &attmat)
  636. {
  637.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  638.         auto &Vertices = LevelSharedVertexState.get_vertices();
  639.         vms_matrix      rotmat,rotmat2,rotmat3;
  640.         vms_vector      forvec,upvec;
  641.  
  642.         // Return if already a face attached on this side.
  643.         if (IS_CHILD(destseg->children[destside]))
  644.                 return 4;
  645.  
  646.         const auto segnum = get_free_segment_number(Segments);
  647.  
  648.         forvec = attmat.fvec;
  649.         upvec = attmat.uvec;
  650.  
  651.         //      We are pretty confident we can add the segment.
  652.         const auto &&nsp = destseg.absolute_sibling(segnum);
  653.  
  654.         nsp->segnum = segnum;
  655.         nsp->objects = object_none;
  656.         nsp->matcen_num = -1;
  657.  
  658.         // Copy group value.
  659.         nsp->group = destseg->group;
  660.  
  661.         // Add segment to proper group list.
  662.         if (nsp->group > -1)
  663.                 add_segment_to_group(nsp, nsp->group);
  664.  
  665.         // Copy the texture map ids.
  666.         copy_tmap_ids(nsp,newseg);
  667.  
  668.         // clear all connections
  669.         for (unsigned side = 0; side < MAX_SIDES_PER_SEGMENT; ++side)
  670.         {
  671.                 nsp->children[side] = segment_none;
  672.                 nsp->shared_segment::sides[side].wall_num = wall_none; 
  673.         }
  674.  
  675.         // Form the connection
  676.         destseg->children[destside] = segnum;
  677. //      destseg->sides[destside].render_flag = 0;
  678.         nsp->children[newside] = destseg;
  679.  
  680.         // Copy vertex indices of the four vertices forming the joint
  681.         auto &dvp = Side_to_verts[destside];
  682.  
  683.         // Set the vertex indices for the four vertices forming the front of the new side
  684.         range_for (const unsigned v, xrange(4u))
  685.                 nsp->verts[v] = destseg->verts[static_cast<int>(dvp[v])];
  686.  
  687.         // The other 4 vertices must be created.
  688.         // Their coordinates are determined by the 4 welded vertices and the vector from front
  689.         // to back of the original *newseg.
  690.  
  691.         // Do lots of hideous matrix stuff, about 3/4 of which could probably be simplified out.
  692.         med_extract_matrix_from_segment(destseg, rotmat);               // get orientation matrix for destseg (orthogonal rotation matrix)
  693.         update_matrix_based_on_side(rotmat,destside);
  694.         const auto rotmat1 = vm_vector_2_matrix(forvec,&upvec,nullptr);
  695.         const auto rotmat4 = vm_matrix_x_matrix(rotmat,rotmat1);                        // this is the desired orientation of the new segment
  696.         med_extract_matrix_from_segment(newseg, rotmat3);               // this is the current orientation of the new segment
  697.         vm_transpose_matrix(rotmat3);                                                           // get the inverse of the current orientation matrix
  698.         vm_matrix_x_matrix(rotmat2,rotmat4,rotmat3);                    // now rotmat2 takes the current segment to the desired orientation
  699.  
  700.         // Warning -- look at this line!
  701.         vm_transpose_matrix(rotmat2);   // added 12:33 pm, 10/01/93
  702.  
  703.         // Compute and rotate the center point of the attaching face.
  704.         auto &vcvertptr = Vertices.vcptr;
  705.         const auto &&vc0 = compute_center_point_on_side(vcvertptr, newseg, newside);
  706.         const auto vr = vm_vec_rotate(vc0,rotmat2);
  707.  
  708.         // Now rotate the free vertices in the segment
  709.         std::array<vertex, 4> tvs;
  710.         range_for (const unsigned v, xrange(4u))
  711.                 vm_vec_rotate(tvs[v], vcvertptr(newseg->verts[v + 4]), rotmat2);
  712.  
  713.         // Now translate the new segment so that the center point of the attaching faces are the same.
  714.         const auto &&vc1 = compute_center_point_on_side(vcvertptr, destseg, destside);
  715.         const auto xlate_vec = vm_vec_sub(vc1,vr);
  716.  
  717.         // Create and add the 4 new vertices.
  718.         range_for (const unsigned v, xrange(4u))
  719.         {
  720.                 vm_vec_add2(tvs[v],xlate_vec);
  721.                 nsp->verts[v+4] = med_add_vertex(tvs[v]);
  722.         }
  723.  
  724.         set_vertex_counts();
  725.  
  726.         // Now all the vertices are in place.  Create the faces.
  727.         validate_segment(vcvertptr, nsp);
  728.  
  729.         //      Say to not render at the joint.
  730. //      destseg->sides[destside].render_flag = 0;
  731. //      nsp->sides[newside].render_flag = 0;
  732.  
  733.         Cursegp = nsp;
  734.  
  735.         return  0;
  736. }
  737.  
  738.  
  739. // ------------------------------------------------------------------------------------------
  740. // Attach side newside of newseg to side destside of destseg.
  741. // Copies *newseg into global array Segments, increments Num_segments.
  742. // Forms a weld between the two segments by making the new segment fit to the old segment.
  743. // Updates number of faces per side if necessitated by new vertex coordinates.
  744. //      Updates Cursegp.
  745. // Return value:
  746. //  0 = successful attach
  747. //  1 = No room in Segments[].
  748. //  2 = No room in Vertices[].
  749. //  3 = newside != WFRONT -- for now, the new segment must be attached at its (own) front side
  750. //       4 = already a face attached on side newside
  751. int med_attach_segment(const vmsegptridx_t destseg, const vmsegptr_t newseg, int destside, int newside)
  752. {
  753.         int             rval;
  754.         const auto ocursegp = Cursegp;
  755.  
  756.         vms_angvec      tang = {0,0,0};
  757.         const auto &&rotmat = vm_angles_2_matrix(tang);
  758.         rval = med_attach_segment_rotated(destseg,newseg,destside,newside,rotmat);
  759.         med_propagate_tmaps_to_segments(ocursegp,Cursegp,0);
  760.         med_propagate_tmaps_to_back_side(Cursegp, Side_opposite[newside],0);
  761.         copy_uvs_seg_to_seg(vmsegptr(&New_segment), Cursegp);
  762.  
  763.         return rval;
  764. }
  765.  
  766. }
  767.  
  768. // -------------------------------------------------------------------------------
  769. //      Delete a vertex, sort of.
  770. //      Decrement the vertex count.  If the count goes to 0, then the vertex is free (has been deleted).
  771. static void delete_vertex(const unsigned v)
  772. {
  773.         Assert(v < MAX_VERTICES);                       // abort if vertex is not in array Vertices
  774.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  775.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  776.         Assert(Vertex_active[v] >= 1);  // abort if trying to delete a non-existent vertex
  777.  
  778.         Vertex_active[v]--;
  779. }
  780.  
  781. // -------------------------------------------------------------------------------
  782. //      Update Num_vertices.
  783. //      This routine should be called by anyone who calls delete_vertex.  It could be called in delete_vertex,
  784. //      but then it would be called much more often than necessary, and it is a slow routine.
  785. static void update_num_vertices(void)
  786. {
  787.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  788.         auto &Vertices = LevelSharedVertexState.get_vertices();
  789.         // Now count the number of vertices.
  790.         unsigned n = 0;
  791.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  792.         range_for (const auto v, partial_range(Vertex_active, Vertices.get_count()))
  793.                 if (v)
  794.                         ++n;
  795.         LevelSharedVertexState.Num_vertices = n;
  796. }
  797.  
  798. namespace dsx {
  799.  
  800. // -------------------------------------------------------------------------------
  801. //      Set Vertex_active to number of occurrences of each vertex.
  802. //      Set Num_vertices.
  803. void set_vertex_counts(void)
  804. {
  805.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  806.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  807.         unsigned Num_vertices = 0;
  808.  
  809.         Vertex_active = {};
  810.  
  811.         // Count number of occurrences of each vertex.
  812.         range_for (const auto &&segp, vmsegptr)
  813.         {
  814.                 if (segp->segnum != segment_none)
  815.                         range_for (auto &v, segp->verts)
  816.                         {
  817.                                 if (!Vertex_active[v])
  818.                                         Num_vertices++;
  819.                                 ++ Vertex_active[v];
  820.                         }
  821.         }
  822.         LevelSharedVertexState.Num_vertices = Num_vertices;
  823. }
  824.  
  825. // -------------------------------------------------------------------------------
  826. //      Delete all vertices in segment *sp from the vertex list if they are not contained in another segment.
  827. //      This is kind of a dangerous routine.  It modifies the global array Vertex_active, using the field as
  828. //      a count.
  829. static void delete_vertices_in_segment(const shared_segment &sp)
  830. {
  831. //      init_vertices();
  832.         set_vertex_counts();
  833.         // Subtract one count for each appearance of vertex in deleted segment
  834.         range_for (auto &v, sp.verts)
  835.                 delete_vertex(v);
  836.  
  837.         update_num_vertices();
  838. }
  839.  
  840. // -------------------------------------------------------------------------------
  841. //      Delete segment *sp in Segments array.
  842. // Return value:
  843. //              0       successfully deleted.
  844. //              1       unable to delete.
  845. int med_delete_segment(const vmsegptridx_t sp)
  846. {
  847.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  848.         auto &Objects = LevelUniqueObjectState.Objects;
  849.         auto &Vertices = LevelSharedVertexState.get_vertices();
  850.         auto &vmobjptr = Objects.vmptr;
  851.         auto &vmobjptridx = Objects.vmptridx;
  852.         segnum_t segnum = sp;
  853.         // Cannot delete segment if only segment.
  854.         if (LevelSharedSegmentState.Num_segments == 1)
  855.                 return 1;
  856.  
  857.         // Don't try to delete if segment doesn't exist.
  858.         if (sp->segnum == segment_none) {
  859.                 return 1;
  860.         }
  861.  
  862.         // Delete its refueling center if it has one
  863.         fuelcen_delete(sp);
  864.  
  865.         delete_vertices_in_segment(sp);
  866.  
  867.         -- LevelSharedSegmentState.Num_segments;
  868.  
  869.         // If deleted segment has walls on any side, wipe out the wall.
  870.         for (unsigned side = 0; side < MAX_SIDES_PER_SEGMENT; ++side)
  871.                 if (sp->shared_segment::sides[side].wall_num != wall_none)
  872.                         wall_remove_side(sp, side);
  873.  
  874.         auto &vcvertptr = Vertices.vcptr;
  875.         // Find out what this segment was connected to and break those connections at the other end.
  876.         range_for (auto &side, sp->children)
  877.                 if (IS_CHILD(side)) {
  878.                         const auto &&csp = sp.absolute_sibling(side);
  879.                         for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
  880.                                 if (csp->children[s] == segnum) {
  881.                                         csp->children[s] = segment_none;                                // this is the side of connection, break it
  882.                                         validate_segment_side(vcvertptr, csp, s);                                       // we have converted a connection to a side so validate the segment
  883.                                         med_propagate_tmaps_to_back_side(csp,s,0);
  884.                                 }
  885.                         Cursegp = csp;
  886.                         med_create_new_segment_from_cursegp();
  887.                         copy_uvs_seg_to_seg(vmsegptr(&New_segment), Cursegp);
  888.                 }
  889.  
  890.         sp->segnum = segment_none;                                                                              // Mark segment as inactive.
  891.  
  892.         // If deleted segment = marked segment, then say there is no marked segment
  893.         if (sp == Markedsegp)
  894.                 Markedsegp = segment_none;
  895.        
  896.         //      If deleted segment = a Group segment ptr, then wipe it out.
  897.         range_for (auto &s, partial_range(Groupsegp, num_groups))
  898.                 if (s == sp)
  899.                         s = nullptr;
  900.  
  901.         // If deleted segment = group segment, wipe it off the group list.
  902.         if (sp->group > -1)
  903.                         delete_segment_from_group(sp, sp->group);
  904.  
  905.         // If we deleted something which was not connected to anything, must now select a new current segment.
  906.         if (Cursegp == sp)
  907.                 for (segnum_t s=0; s<MAX_SEGMENTS; s++)
  908.                         if ((Segments[s].segnum != segment_none) && (s!=segnum) ) {
  909.                                 Cursegp = imsegptridx(s);
  910.                                 med_create_new_segment_from_cursegp();
  911.                         break;
  912.                         }
  913.  
  914.         // If deleted segment contains objects, wipe out all objects
  915.                 range_for (const auto objnum, objects_in(*sp, vmobjptridx, vmsegptr))
  916.                 {
  917.                         //if an object is in the seg, delete it
  918.                         //if the object is the player, move to new curseg
  919.                         if (objnum == ConsoleObject)    {
  920.                                 compute_segment_center(vcvertptr, ConsoleObject->pos,Cursegp);
  921.                                 obj_relink(vmobjptr, vmsegptr, objnum, Cursegp);
  922.                         } else
  923.                                 obj_delete(LevelUniqueObjectState, Segments, objnum);
  924.                 }
  925.  
  926.         // Make sure everything deleted ok...
  927.         Assert( sp->objects==object_none );
  928.  
  929.         // If we are leaving many holes in Segments or Vertices, then compress mine, because it is inefficient to be that way
  930. //      if ((Highest_segment_index > Num_segments+4) || (Highest_vertex_index > Num_vertices+4*8))
  931. //              med_compress_mine();
  932.  
  933.         return 0;
  934. }
  935.  
  936. // ------------------------------------------------------------------------------------------
  937. //      Copy texture maps from sseg to dseg
  938. static void copy_tmaps_to_segment(segment &dstseg, const segment &srcseg)
  939. {
  940.         shared_segment &shared_dst_seg = dstseg;
  941.         unique_segment &unique_dst_seg = dstseg;
  942.         const shared_segment &shared_src_seg = srcseg;
  943.         const unique_segment &unique_src_seg = srcseg;
  944.         range_for (const auto &&z, zip(shared_src_seg.sides, shared_dst_seg.sides, unique_src_seg.sides, unique_dst_seg.sides))
  945.         {
  946.                 auto &shared_src_side = std::get<0>(z);
  947.                 auto &shared_dst_side = std::get<1>(z);
  948.                 auto &unique_src_side = std::get<2>(z);
  949.                 auto &unique_dst_side = std::get<3>(z);
  950.                 shared_dst_side.set_type(shared_src_side.get_type());
  951.                 unique_dst_side.tmap_num = unique_src_side.tmap_num;
  952.                 unique_dst_side.tmap_num2 = unique_src_side.tmap_num2;
  953.         }
  954.  
  955. }
  956.  
  957. // ------------------------------------------------------------------------------------------
  958. // Rotate the segment *seg by the pitch, bank, heading defined by *rot, destructively
  959. // modifying its four free vertices in the global array Vertices.
  960. // It is illegal to rotate a segment which has connectivity != 1.
  961. // Pitch, bank, heading are about the point which is the average of the four points
  962. // forming the side of connection.
  963. // Return value:
  964. //  0 = successful rotation
  965. //  1 = Connectivity makes rotation illegal (connected to 0 or 2+ segments)
  966. //  2 = Rotation causes degeneracy, such as self-intersecting segment.
  967. //       3 = Unable to rotate because not connected to exactly 1 segment.
  968. int med_rotate_segment(const vmsegptridx_t seg, const vms_matrix &rotmat)
  969. {
  970.         int             newside=0,destside;
  971.         int             count;
  972.  
  973.         // Find side of attachment
  974.         count = 0;
  975.         range_for (const auto &&es, enumerate(seg->children))
  976.                 if (IS_CHILD(es.value))
  977.                 {
  978.                         count++;
  979.                         newside = es.idx;
  980.                 }
  981.  
  982.         // Return if passed in segment is connected to other than 1 segment.
  983.         if (count != 1)
  984.                 return 3;
  985.  
  986.         const auto &&destseg = seg.absolute_sibling(seg->children[newside]);
  987.  
  988.         destside = 0;
  989.         while (destside < MAX_SIDES_PER_SEGMENT && destseg->children[destside] != seg)
  990.                 destside++;
  991.                
  992.         // Before deleting the segment, copy its texture maps to New_segment
  993.         copy_tmaps_to_segment(vmsegptr(&New_segment), seg);
  994.  
  995.         if (Curside == WFRONT)
  996.                 Curside = WBACK;
  997.  
  998.         med_attach_segment_rotated(destseg, vmsegptr(&New_segment), destside, AttachSide, rotmat);
  999.  
  1000.         //      Save tmap_num on each side to restore after call to med_propagate_tmaps_to_segments and _back_side
  1001.         //      which will change the tmap nums.
  1002.         std::array<int16_t, MAX_SIDES_PER_SEGMENT> side_tmaps;
  1003.         range_for (const auto &&z, zip(side_tmaps, seg->unique_segment::sides))
  1004.         {
  1005.                 const unique_side &us = std::get<1>(z);
  1006.                 std::get<0>(z) = us.tmap_num;
  1007.         }
  1008.  
  1009.         auto back_side = Side_opposite[find_connect_side(destseg, seg)];
  1010.  
  1011.         med_propagate_tmaps_to_segments(destseg, seg,0);
  1012.         med_propagate_tmaps_to_back_side(seg, back_side,0);
  1013.  
  1014.         for (const auto &&[idx, side_tmap, us] : enumerate(zip(side_tmaps, seg->unique_segment::sides)))
  1015.                 if (idx != back_side)
  1016.                 {
  1017.                         us.tmap_num = side_tmap;
  1018.                 }
  1019.  
  1020.         return  0;
  1021. }
  1022.  
  1023. // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  1024.  
  1025. // ----------------------------------------------------------------------------
  1026. //      Compute the sum of the distances between the four pairs of points.
  1027. //      The connections are:
  1028. //              firstv1 : 0             (firstv1+1)%4 : 1               (firstv1+2)%4 : 2               (firstv1+3)%4 : 3
  1029. static fix seg_seg_vertex_distsum(const vcsegptr_t seg1, const unsigned side1, const vcsegptr_t seg2, const unsigned side2, const unsigned firstv1)
  1030. {
  1031.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1032.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1033.         fix     distsum;
  1034.  
  1035.         distsum = 0;
  1036.         auto &vcvertptr = Vertices.vcptr;
  1037.         range_for (const unsigned secondv, xrange(4u))
  1038.         {
  1039.                 const unsigned firstv = (4 - secondv + (3 - firstv1)) % 4;
  1040.                 distsum += vm_vec_dist(vcvertptr(seg1->verts[Side_to_verts[side1][firstv]]),vcvertptr(seg2->verts[Side_to_verts[side2][secondv]]));
  1041.         }
  1042.  
  1043.         return distsum;
  1044.  
  1045. }
  1046.  
  1047. // ----------------------------------------------------------------------------
  1048. //      Determine how to connect two segments together with the least amount of twisting.
  1049. //      Returns vertex index in 0..3 on first segment.  Assumed ordering of vertices
  1050. //      on second segment is 0,1,2,3.
  1051. //      So, if return value is 2, connect 2:0 3:1 0:2 1:3.
  1052. //      Theory:
  1053. //              We select an ordering of vertices for connection.  For the first pair of vertices to be connected,
  1054. //              compute the vector.  For the three remaining pairs of vertices, compute the vectors from one vertex
  1055. //              to the other.  Compute the dot products of these vectors with the original vector.  Add them up.
  1056. //              The close we are to 3, the better fit we have.  Reason:  The largest value for the dot product is
  1057. //              1.0, and this occurs for a parallel set of vectors.
  1058. static int get_index_of_best_fit(const vcsegptr_t seg1, int side1, const vcsegptr_t seg2, int side2)
  1059. {
  1060.         int     firstv;
  1061.         fix     min_distance;
  1062.         int     best_index=0;
  1063.  
  1064.         min_distance = F1_0*30000;
  1065.  
  1066.         for (firstv=0; firstv<4; firstv++) {
  1067.                 fix t;
  1068.                 t = seg_seg_vertex_distsum(seg1, side1, seg2, side2, firstv);
  1069.                 if (t <= min_distance) {
  1070.                         min_distance = t;
  1071.                         best_index = firstv;
  1072.                 }
  1073.         }
  1074.  
  1075.         return best_index;
  1076.  
  1077. }
  1078.  
  1079.  
  1080. #define MAX_VALIDATIONS 50
  1081.  
  1082. // ----------------------------------------------------------------------------
  1083. //      Remap uv coordinates in all sides in segment *sp which have a vertex in vp[4].
  1084. //      vp contains absolute vertex indices.
  1085. static void remap_side_uvs(const vmsegptridx_t sp, const std::array<int, 4> &vp)
  1086. {
  1087.         range_for (const auto &&es, enumerate(Side_to_verts))
  1088.         {
  1089.                 range_for (const auto v, es.value)
  1090.                         range_for (auto &i, vp) // scan each vertex in vp[4]
  1091.                                 if (v == i) {
  1092.                                         assign_default_uvs_to_side(sp, es.idx);                                 // Side s needs to be remapped
  1093.                                         goto next_side;
  1094.                                 }
  1095. next_side: ;
  1096.         }
  1097. }
  1098.  
  1099. // ----------------------------------------------------------------------------
  1100. //      Modify seg2 to share side2 with seg1:side1.  This forms a connection between
  1101. //      two segments without creating a new segment.  It modifies seg2 by sharing
  1102. //      vertices from seg1.  seg1 is not modified.  Four vertices from seg2 are
  1103. //      deleted.
  1104. //      Return code:
  1105. //              0                       joint formed
  1106. //              1                       -- no, this is legal! -- unable to form joint because one or more vertices of side2 is not free
  1107. //              2                       unable to form joint because side1 is already used
  1108. int med_form_joint(const vmsegptridx_t seg1, int side1, const vmsegptridx_t seg2, int side2)
  1109. {
  1110.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1111.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1112.         int             bfi,v,s1;
  1113.         std::array<int, 4> lost_vertices, remap_vertices;
  1114.         std::array<segnum_t, MAX_VALIDATIONS> validation_list;
  1115.         uint_fast32_t nv;
  1116.  
  1117.         //      Make sure that neither side is connected.
  1118.         if (IS_CHILD(seg1->children[side1]) || IS_CHILD(seg2->children[side2]))
  1119.                 return 2;
  1120.  
  1121.         // Make sure there is no wall there
  1122.         if ((seg1->shared_segment::sides[side1].wall_num != wall_none) || (seg2->shared_segment::sides[side2].wall_num != wall_none))
  1123.                 return 2;
  1124.  
  1125.         //      We can form the joint.  Find the best orientation of vertices.
  1126.         bfi = get_index_of_best_fit(seg1, side1, seg2, side2);
  1127.  
  1128.         auto &vp1 = Side_to_verts[side1];
  1129.         auto &vp2 = Side_to_verts[side2];
  1130.  
  1131.         //      Make a copy of the list of vertices in seg2 which will be deleted and set the
  1132.         //      associated vertex number, so that all occurrences of the vertices can be replaced.
  1133.         for (v=0; v<4; v++)
  1134.                 lost_vertices[v] = seg2->verts[static_cast<int>(vp2[v])];
  1135.  
  1136.         //      Now, for each vertex in lost_vertices, determine which vertex it maps to.
  1137.         for (v=0; v<4; v++)
  1138.                 remap_vertices[3 - ((v + bfi) % 4)] = seg1->verts[static_cast<int>(vp1[v])];
  1139.  
  1140.         // Now, in all segments, replace all occurrences of vertices in lost_vertices with remap_vertices
  1141.  
  1142.         // Put the one segment we know are being modified into the validation list.
  1143.         // Note: seg1 does not require a full validation, only a validation of the affected side.  Its vertices do not move.
  1144.         nv = 1;
  1145.         validation_list[0] = seg2;
  1146.  
  1147.         for (v=0; v<4; v++)
  1148.                 range_for (const auto &&segp, vmsegptridx)
  1149.                 {
  1150.                         if (segp->segnum != segment_none)
  1151.                                 range_for (auto &sv, segp->verts)
  1152.                                         if (sv == lost_vertices[v]) {
  1153.                                                 sv = remap_vertices[v];
  1154.                                                 // Add segment to list of segments to be validated.
  1155.                                                 for (s1=0; s1<nv; s1++)
  1156.                                                         if (validation_list[s1] == segp)
  1157.                                                                 break;
  1158.                                                 if (s1 == nv)
  1159.                                                         validation_list[nv++] = segp;
  1160.                                                 Assert(nv < MAX_VALIDATIONS);
  1161.                                         }
  1162.                 }
  1163.  
  1164.         //      Form new connections.
  1165.         seg1->children[side1] = seg2;
  1166.         seg2->children[side2] = seg1;
  1167.  
  1168.         // validate all segments
  1169.         auto &vcvertptr = Vertices.vcptr;
  1170.         validate_segment_side(vcvertptr, seg1, side1);
  1171.         range_for (auto &s, partial_const_range(validation_list, nv))
  1172.         {
  1173.                 const auto &&segp = seg1.absolute_sibling(s);
  1174.                 validate_segment(vcvertptr, segp);
  1175.                 remap_side_uvs(segp, remap_vertices);   // remap uv coordinates on sides which were reshaped (ie, have a vertex in lost_vertices)
  1176.                 warn_if_concave_segment(segp);
  1177.         }
  1178.  
  1179.         set_vertex_counts();
  1180.  
  1181.         return 0;
  1182. }
  1183.  
  1184. // ----------------------------------------------------------------------------
  1185. //      Create a new segment and use it to form a bridge between two existing segments.
  1186. //      Specify two segment:side pairs.  If either segment:side is not open (ie, segment->children[side] != -1)
  1187. //      then it is not legal to form the brider.
  1188. //      Return:
  1189. //              0       bridge segment formed
  1190. //              1       unable to form bridge because one (or both) of the sides is not open.
  1191. //      Note that no new vertices are created by this process.
  1192. int med_form_bridge_segment(const vmsegptridx_t seg1, int side1, const vmsegptridx_t seg2, int side2)
  1193. {
  1194.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1195.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1196.         int                     v,bfi;
  1197.  
  1198.         if (IS_CHILD(seg1->children[side1]) || IS_CHILD(seg2->children[side2]))
  1199.                 return 1;
  1200.  
  1201.         const auto &&bs = seg1.absolute_sibling(get_free_segment_number(Segments));
  1202.         shared_segment &sbs = *bs;
  1203.         sbs.segnum = bs;
  1204.         bs->objects = object_none;
  1205.  
  1206.         // Copy vertices from seg2 into last 4 vertices of bridge segment.
  1207.         {
  1208.         auto &sv = Side_to_verts[side2];
  1209.         for (v=0; v<4; v++)
  1210.                 sbs.verts[(3-v)+4] = seg2->verts[static_cast<int>(sv[v])];
  1211.         }
  1212.  
  1213.         // Copy vertices from seg1 into first 4 vertices of bridge segment.
  1214.         bfi = get_index_of_best_fit(seg1, side1, seg2, side2);
  1215.  
  1216.         {
  1217.         auto &sv = Side_to_verts[side1];
  1218.         for (v=0; v<4; v++)
  1219.                 bs->verts[(v + bfi) % 4] = seg1->verts[static_cast<int>(sv[v])];
  1220.         }
  1221.  
  1222.         // Form connections to children, first initialize all to unconnected.
  1223.         range_for (const auto &&z, zip(sbs.children, sbs.sides))
  1224.         {
  1225.                 std::get<0>(z) = segment_none;
  1226.                 std::get<1>(z).wall_num = wall_none;
  1227.         }
  1228.  
  1229.         // Now form connections between segments.
  1230.  
  1231.         bs->children[AttachSide] = seg1;
  1232.         bs->children[Side_opposite[AttachSide]] = seg2;
  1233.  
  1234.         seg1->children[side1] = bs; //seg2 - Segments;
  1235.         seg2->children[side2] = bs; //seg1 - Segments;
  1236.  
  1237.         //      Validate bridge segment, and if degenerate, clean up mess.
  1238.         Degenerate_segment_found = 0;
  1239.  
  1240.         auto &vcvertptr = Vertices.vcptr;
  1241.         validate_segment(vcvertptr, bs);
  1242.  
  1243.         if (Degenerate_segment_found) {
  1244.                 seg1->children[side1] = segment_none;
  1245.                 seg2->children[side2] = segment_none;
  1246.                 bs->children[AttachSide] = segment_none;
  1247.                 bs->children[static_cast<int>(Side_opposite[AttachSide])] = segment_none;
  1248.                 if (med_delete_segment(bs)) {
  1249.                         Int3();
  1250.                 }
  1251.                 editor_status("Bridge segment would be degenerate, not created.\n");
  1252.                 return 1;
  1253.         } else {
  1254.                 validate_segment(vcvertptr, seg1);      // used to only validate side, but segment does more error checking: ,side1);
  1255.                 validate_segment(vcvertptr, seg2);      // ,side2);
  1256.                 med_propagate_tmaps_to_segments(seg1,bs,0);
  1257.  
  1258.                 editor_status("Bridge segment formed.");
  1259.                 warn_if_concave_segment(bs);
  1260.                 return 0;
  1261.         }
  1262. }
  1263.  
  1264. // -------------------------------------------------------------------------------
  1265. //      Create a segment given center, dimensions, rotation matrix.
  1266. //      Note that the created segment will always have planar sides and rectangular cross sections.
  1267. //      It will be created with walls on all sides, ie not connected to anything.
  1268. void med_create_segment(const vmsegptridx_t sp,fix cx, fix cy, fix cz, fix length, fix width, fix height, const vms_matrix &mp)
  1269. {
  1270.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1271.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1272.         int                     f;
  1273.         ++ LevelSharedSegmentState.Num_segments;
  1274.  
  1275.         sp->segnum = 1;                                         // What to put here?  I don't know.
  1276.  
  1277.         // Form connections to children, of which it has none.
  1278.         for (unsigned i = 0; i < MAX_SIDES_PER_SEGMENT; ++i)
  1279.         {
  1280.                 sp->children[i] = segment_none;
  1281. //              sp->sides[i].render_flag = 0;
  1282.                 sp->shared_segment::sides[i].wall_num  = wall_none;
  1283.         }
  1284.  
  1285.         sp->group = -1;
  1286.         sp->matcen_num = -1;
  1287.  
  1288.         //      Create relative-to-center vertices, which are the rotated points on the box defined by length, width, height
  1289.         sp->verts[0] = med_add_vertex(vertex{vm_vec_rotate({+width/2, +height/2, -length/2}, mp)});
  1290.         sp->verts[1] = med_add_vertex(vertex{vm_vec_rotate({+width/2, -height/2, -length/2}, mp)});
  1291.         sp->verts[2] = med_add_vertex(vertex{vm_vec_rotate({-width/2, -height/2, -length/2}, mp)});
  1292.         sp->verts[3] = med_add_vertex(vertex{vm_vec_rotate({-width/2, +height/2, -length/2}, mp)});
  1293.         sp->verts[4] = med_add_vertex(vertex{vm_vec_rotate({+width/2, +height/2, +length/2}, mp)});
  1294.         sp->verts[5] = med_add_vertex(vertex{vm_vec_rotate({+width/2, -height/2, +length/2}, mp)});
  1295.         sp->verts[6] = med_add_vertex(vertex{vm_vec_rotate({-width/2, -height/2, +length/2}, mp)});
  1296.         sp->verts[7] = med_add_vertex(vertex{vm_vec_rotate({-width/2, +height/2, +length/2}, mp)});
  1297.  
  1298.         // Now create the vector which is the center of the segment and add that to all vertices.
  1299.         const vms_vector cv{cx, cy, cz};
  1300.  
  1301.         //      Now, add the center to all vertices, placing the segment in 3 space.
  1302.         auto &vmvertptr = Vertices.vmptr;
  1303.         range_for (auto &i, sp->verts)
  1304.                 vm_vec_add2(vmvertptr(i), cv);
  1305.  
  1306.         //      Set scale vector.
  1307. //      sp->scale.x = width;
  1308. //      sp->scale.y = height;
  1309. //      sp->scale.z = length;
  1310.  
  1311.         //      Add faces to all sides.
  1312.         auto &vcvertptr = Vertices.vcptr;
  1313.         for (f=0; f<MAX_SIDES_PER_SEGMENT; f++)
  1314.                 create_walls_on_side(vcvertptr, sp, f);
  1315.  
  1316.         sp->objects = object_none;              //no objects in this segment
  1317.  
  1318.         // Assume nothing special about this segment
  1319.         sp->special = 0;
  1320.         sp->station_idx = station_none;
  1321.         sp->static_light = 0;
  1322.         sp->matcen_num = -1;
  1323.  
  1324.         copy_tmaps_to_segment(sp, vcsegptr(&New_segment));
  1325.  
  1326.         assign_default_uvs_to_segment(sp);
  1327. }
  1328.  
  1329. // ----------------------------------------------------------------------------------------------
  1330. //      Create New_segment using a specified scale factor.
  1331. void med_create_new_segment(const vms_vector &scale)
  1332. {
  1333.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1334.         int                     t;
  1335.         const auto &&sp = vmsegptridx(&New_segment);
  1336.         fix                     length,width,height;
  1337.  
  1338.         length = scale.z;
  1339.         width = scale.x;
  1340.         height = scale.y;
  1341.  
  1342.         sp->segnum = 1;                                         // What to put here?  I don't know.
  1343.  
  1344.         //      Create relative-to-center vertices, which are the points on the box defined by length, width, height
  1345.         t = LevelSharedVertexState.Num_vertices;
  1346.         sp->verts[0] = med_set_vertex(NEW_SEGMENT_VERTICES+0,{+width/2,+height/2,-length/2});
  1347.         sp->verts[1] = med_set_vertex(NEW_SEGMENT_VERTICES+1,{+width/2,-height/2,-length/2});
  1348.         sp->verts[2] = med_set_vertex(NEW_SEGMENT_VERTICES+2,{-width/2,-height/2,-length/2});
  1349.         sp->verts[3] = med_set_vertex(NEW_SEGMENT_VERTICES+3,{-width/2,+height/2,-length/2});
  1350.         sp->verts[4] = med_set_vertex(NEW_SEGMENT_VERTICES+4,{+width/2,+height/2,+length/2});
  1351.         sp->verts[5] = med_set_vertex(NEW_SEGMENT_VERTICES+5,{+width/2,-height/2,+length/2});
  1352.         sp->verts[6] = med_set_vertex(NEW_SEGMENT_VERTICES+6,{-width/2,-height/2,+length/2});
  1353.         sp->verts[7] = med_set_vertex(NEW_SEGMENT_VERTICES+7,{-width/2,+height/2,+length/2});
  1354.         LevelSharedVertexState.Num_vertices = t;
  1355.  
  1356. //      sp->scale = *scale;
  1357.  
  1358.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1359.         auto &vcvertptr = Vertices.vcptr;
  1360.         // Form connections to children, of which it has none, init faces and tmaps.
  1361.         for (const auto &&[s, child, ss, us] : enumerate(zip(sp->children, sp->shared_segment::sides, sp->unique_segment::sides)))
  1362.         {
  1363.                 child = segment_none;
  1364.                 ss.wall_num = wall_none;
  1365.                 create_walls_on_side(vcvertptr, sp, s);
  1366.                 us.tmap_num = s + 1;                                    // assign some stupid old tmap to this side.
  1367.                 us.tmap_num2 = 0;
  1368.         }
  1369.  
  1370.         Seg_orientation = {};
  1371.  
  1372.         sp->objects = object_none;              //no objects in this segment
  1373.  
  1374.         assign_default_uvs_to_segment(sp);
  1375.  
  1376.         // Assume nothing special about this segment
  1377.         sp->special = 0;
  1378.         sp->station_idx = station_none;
  1379.         sp->static_light = 0;
  1380.         sp->matcen_num = -1;
  1381. }
  1382.  
  1383. // -------------------------------------------------------------------------------
  1384. void med_create_new_segment_from_cursegp(void)
  1385. {
  1386.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1387.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1388.         vms_vector      scalevec;
  1389.         vms_vector      uvec, rvec, fvec;
  1390.  
  1391.         med_extract_up_vector_from_segment_side(Cursegp, Curside, uvec);
  1392.         med_extract_right_vector_from_segment_side(Cursegp, Curside, rvec);
  1393.         auto &vcvertptr = Vertices.vcptr;
  1394.         extract_forward_vector_from_segment(vcvertptr, Cursegp, fvec);
  1395.  
  1396.         scalevec.x = vm_vec_mag(rvec);
  1397.         scalevec.y = vm_vec_mag(uvec);
  1398.         scalevec.z = vm_vec_mag(fvec);
  1399.         med_create_new_segment(scalevec);
  1400. }
  1401.  
  1402. // -------------------------------------------------------------------------------
  1403. //      Initialize all vertices to inactive status.
  1404. void init_all_vertices(void)
  1405. {
  1406.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1407.         auto &Vertex_active = LevelSharedVertexState.get_vertex_active();
  1408.         Vertex_active = {};
  1409.         range_for (auto &s, Segments)
  1410.                 s.segnum = segment_none;
  1411. }
  1412.  
  1413. // -----------------------------------------------------------------------------
  1414. //      Create coordinate axes in orientation of specified segment, stores vertices at *vp.
  1415. void create_coordinate_axes_from_segment(const vmsegptr_t sp, std::array<unsigned, 16> &vertnums)
  1416. {
  1417.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1418.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1419.         vms_matrix      rotmat;
  1420.         vms_vector t;
  1421.  
  1422.         med_extract_matrix_from_segment(sp, rotmat);
  1423.  
  1424.         auto &vcvertptr = Vertices.vcptr;
  1425.         auto &vmvertptr = Vertices.vmptr;
  1426.         const auto &&v0 = vmvertptr(vertnums[0]);
  1427.         compute_segment_center(vcvertptr, v0, sp);
  1428.  
  1429.         t = rotmat.rvec;
  1430.         vm_vec_scale(t,i2f(32));
  1431.         vm_vec_add(vmvertptr(vertnums[1]), v0, t);
  1432.  
  1433.         t = rotmat.uvec;
  1434.         vm_vec_scale(t,i2f(32));
  1435.         vm_vec_add(vmvertptr(vertnums[2]), v0, t);
  1436.  
  1437.         t = rotmat.fvec;
  1438.         vm_vec_scale(t,i2f(32));
  1439.         vm_vec_add(vmvertptr(vertnums[3]), v0, t);
  1440. }
  1441.  
  1442. // -----------------------------------------------------------------------------
  1443. //      Determine if a segment is concave. Returns true if concave
  1444. static int check_seg_concavity(const vcsegptr_t s)
  1445. {
  1446.         vms_vector n0;
  1447.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1448.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1449.         auto &vcvertptr = Vertices.vcptr;
  1450.         range_for (auto &sn, Side_to_verts)
  1451.                 for (unsigned vn = 0; vn <= 4; ++vn)
  1452.                 {
  1453.                         const auto n1 = vm_vec_normal(
  1454.                                 vcvertptr(s->verts[sn[vn % 4]]),
  1455.                                 vcvertptr(s->verts[sn[(vn + 1) % 4]]),
  1456.                                 vcvertptr(s->verts[sn[(vn + 2) % 4]]));
  1457.  
  1458.                         //vm_vec_normalize(&n1);
  1459.  
  1460.                         if (vn>0) if (vm_vec_dot(n0,n1) < f0_5) return 1;
  1461.  
  1462.                         n0 = n1;
  1463.                 }
  1464.  
  1465.         return 0;
  1466. }
  1467.  
  1468.  
  1469. // -----------------------------------------------------------------------------
  1470. //      Find all concave segments and add to list
  1471. void find_concave_segs()
  1472. {
  1473.         Warning_segs.clear();
  1474.  
  1475.         range_for (const auto &&s, vcsegptridx)
  1476.                 if (s->segnum != segment_none)
  1477.                         if (check_seg_concavity(s))
  1478.                                 Warning_segs.emplace_back(s);
  1479. }
  1480.  
  1481.  
  1482. // -----------------------------------------------------------------------------
  1483. void warn_if_concave_segments(void)
  1484. {
  1485.         find_concave_segs();
  1486.  
  1487.         if (!Warning_segs.empty())
  1488.         {
  1489.                 editor_status_fmt("*** WARNING *** %d concave segments in mine! *** WARNING ***", Warning_segs.size());
  1490.     }
  1491. }
  1492.  
  1493. // -----------------------------------------------------------------------------
  1494. //      Check segment s, if concave, warn
  1495. void warn_if_concave_segment(const vmsegptridx_t s)
  1496. {
  1497.         int     result;
  1498.  
  1499.         result = check_seg_concavity(s);
  1500.  
  1501.         if (result) {
  1502.                 Warning_segs.emplace_back(s);
  1503.  
  1504.                         editor_status("*** WARNING *** New segment is concave! *** WARNING ***");
  1505.         } //else
  1506.         //editor_status("");
  1507. }
  1508.  
  1509.  
  1510. // -------------------------------------------------------------------------------
  1511. //      Find segment adjacent to sp:side.
  1512. //      Adjacent means a segment which shares all four vertices.
  1513. //      Return true if segment found and fill in segment in adj_sp and side in adj_side.
  1514. //      Return false if unable to find, in which case adj_sp and adj_side are undefined.
  1515. int med_find_adjacent_segment_side(const vmsegptridx_t sp, int side, imsegptridx_t &adj_sp, int *adj_side)
  1516. {
  1517.         std::array<int, 4> abs_verts;
  1518.  
  1519.         //      Stuff abs_verts[4] array with absolute vertex indices
  1520.         range_for (const unsigned v, xrange(4u))
  1521.                 abs_verts[v] = sp->verts[Side_to_verts[side][v]];
  1522.  
  1523.         //      Scan all segments, looking for a segment which contains the four abs_verts
  1524.         range_for (const auto &&segp, vmsegptridx)
  1525.         {
  1526.                 if (segp != sp)
  1527.                 {
  1528.                         range_for (auto &v, abs_verts)
  1529.                         {                                                                                               // do for each vertex in abs_verts
  1530.                                 range_for (auto &vv, segp->verts) // do for each vertex in segment
  1531.                                         if (v == vv)
  1532.                                                 goto fass_found1;                                                                                       // Current vertex (indexed by v) is present in segment, try next
  1533.                                 goto fass_next_seg;                                                                                             // This segment doesn't contain the vertex indexed by v
  1534.                         fass_found1: ;
  1535.                         }               // end for v
  1536.  
  1537.                         //      All four vertices in sp:side are present in segment seg.
  1538.                         //      Determine side and return
  1539.                         range_for (const auto &&es, enumerate(Side_to_verts))
  1540.                         {
  1541.                                 range_for (const auto v, es.value)
  1542.                                 {
  1543.                                         range_for (auto &vv, abs_verts)
  1544.                                         {
  1545.                                                 if (segp->verts[v] == vv)
  1546.                                                         goto fass_found2;
  1547.                                         }
  1548.                                         goto fass_next_side;                                                                                    // Couldn't find vertex v in current side, so try next side.
  1549.                                 fass_found2: ;
  1550.                                 }
  1551.                                 // Found all four vertices in current side.  We are done!
  1552.                                 adj_sp = segp;
  1553.                                 *adj_side = es.idx;
  1554.                                 return 1;
  1555.                         fass_next_side: ;
  1556.                         }
  1557.                         Assert(0);      // Impossible -- we identified this segment as containing all 4 vertices of side "side", but we couldn't find them.
  1558.                         return 0;
  1559.                 fass_next_seg: ;
  1560.                 }
  1561.         }
  1562.  
  1563.         return 0;
  1564. }
  1565.  
  1566.  
  1567. #define JOINT_THRESHOLD 10000*F1_0              // (Huge threshold)
  1568.  
  1569. // -------------------------------------------------------------------------------
  1570. //      Find segment closest to sp:side.
  1571. //      Return true if segment found and fill in segment in adj_sp and side in adj_side.
  1572. //      Return false if unable to find, in which case adj_sp and adj_side are undefined.
  1573. int med_find_closest_threshold_segment_side(const vmsegptridx_t sp, int side, imsegptridx_t &adj_sp, int *adj_side, fix threshold)
  1574. {
  1575.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1576.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1577.         fix                     current_dist, closest_seg_dist;
  1578.  
  1579.         if (IS_CHILD(sp->children[side]))
  1580.                 return 0;
  1581.  
  1582.         auto &vcvertptr = Vertices.vcptr;
  1583.         const auto &&vsc = compute_center_point_on_side(vcvertptr, sp, side);
  1584.  
  1585.         closest_seg_dist = JOINT_THRESHOLD;
  1586.  
  1587.         //      Scan all segments, looking for a segment which contains the four abs_verts
  1588.         range_for (const auto &&segp, vmsegptridx)
  1589.         {
  1590.                 if (segp != sp)
  1591.                         range_for (const auto &&es, enumerate(segp->children))
  1592.                         {
  1593.                                 if (!IS_CHILD(es.value))
  1594.                                 {
  1595.                                         const auto &&vtc = compute_center_point_on_side(vcvertptr, segp, es.idx);
  1596.                                         current_dist = vm_vec_dist( vsc, vtc );
  1597.                                         if (current_dist < closest_seg_dist) {
  1598.                                                 adj_sp = segp;
  1599.                                                 *adj_side = es.idx;
  1600.                                                 closest_seg_dist = current_dist;
  1601.                                         }
  1602.                                 }
  1603.                         }      
  1604.         }
  1605.  
  1606.         if (closest_seg_dist < threshold)
  1607.                 return 1;
  1608.         else
  1609.                 return 0;
  1610. }
  1611.  
  1612. }
  1613.