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.  * New home for find_vector_intersection()
  23.  *
  24.  */
  25.  
  26. #include <algorithm>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include "pstypes.h"
  31. #include "u_mem.h"
  32. #include "dxxerror.h"
  33. #include "inferno.h"
  34. #include "fvi.h"
  35. #include "segment.h"
  36. #include "object.h"
  37. #include "wall.h"
  38. #include "laser.h"
  39. #include "gameseg.h"
  40. #include "rle.h"
  41. #include "robot.h"
  42. #include "piggy.h"
  43. #include "player.h"
  44. #include "compiler-range_for.h"
  45. #include "segiter.h"
  46.  
  47. using std::min;
  48.  
  49. #define face_type_num(nfaces,face_num,tri_edge) ((nfaces==1)?0:(tri_edge*2 + face_num))
  50.  
  51. //find the point on the specified plane where the line intersects
  52. //returns true if point found, false if line parallel to plane
  53. //new_pnt is the found point on the plane
  54. //plane_pnt & plane_norm describe the plane
  55. //p0 & p1 are the ends of the line
  56. __attribute_warn_unused_result
  57. static int find_plane_line_intersection(vms_vector &new_pnt,const vms_vector &plane_pnt,const vms_vector &plane_norm,const vms_vector &p0,const vms_vector &p1,fix rad)
  58. {
  59.         auto d = vm_vec_sub(p1,p0);
  60.         const fix den = -vm_vec_dot(plane_norm,d);
  61.         if (unlikely(!den)) // moving parallel to wall, so can't hit it
  62.                 return 0;
  63.  
  64.         const auto w = vm_vec_sub(p0,plane_pnt);
  65.         fix num = vm_vec_dot(plane_norm,w) - rad; //move point out by rad
  66.  
  67.         //check for various bad values
  68.         if (den > 0 && (-num>>15) >= den) //will overflow (large negative)
  69.                 num = (f1_0-f0_5)*den;
  70.         if (den > 0 && num > den) //frac greater than one
  71.                 return 0;
  72.         if (den < 0 && num < den) //frac greater than one
  73.                 return 0;
  74.         if (labs (num) / (f1_0 / 2) >= labs (den))
  75.                 return 0;
  76.  
  77.         vm_vec_scale2(d,num,den);
  78.         vm_vec_add(new_pnt,p0,d);
  79.  
  80.         return 1;
  81.  
  82. }
  83.  
  84. namespace {
  85.  
  86. struct vec2d {
  87.         fix i,j;
  88. };
  89.  
  90. //intersection types
  91. #define IT_NONE 0       //doesn't touch face at all
  92. #define IT_FACE 1       //touches face
  93. #define IT_EDGE 2       //touches edge of face
  94. #define IT_POINT        3       //touches vertex
  95.  
  96. struct ij_pair
  97. {
  98.         fix vms_vector::*largest_normal;
  99.         fix vms_vector::*i;
  100.         fix vms_vector::*j;
  101. };
  102.  
  103. }
  104.  
  105. __attribute_warn_unused_result
  106. static ij_pair find_largest_normal(vms_vector t)
  107. {
  108.         t.x = labs(t.x);
  109.         t.y = labs(t.y);
  110.         t.z = labs(t.z);
  111.         if (t.x > t.y)
  112.         {
  113.                 if (t.x > t.z)
  114.                         return {&vms_vector::x, &vms_vector::z, &vms_vector::y};
  115.         }
  116.         else if (t.y > t.z)
  117.                 return {&vms_vector::y, &vms_vector::x, &vms_vector::z};
  118.         return {&vms_vector::z, &vms_vector::y, &vms_vector::x};
  119. }
  120.  
  121. //see if a point in inside a face by projecting into 2d
  122. __attribute_warn_unused_result
  123. static unsigned check_point_to_face(const vms_vector &checkp, const vms_vector &norm, const unsigned facenum, const unsigned nv, const vertex_array_list_t &vertex_list)
  124. {
  125.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  126.         auto &Vertices = LevelSharedVertexState.get_vertices();
  127. ///
  128.         int edge;
  129.         uint edgemask;
  130.         fix check_i,check_j;
  131.         //now do 2d check to see if point is in side
  132.  
  133.         //project polygon onto plane by finding largest component of normal
  134.         ij_pair ij = find_largest_normal(norm);
  135.         if (norm.*ij.largest_normal <= 0)
  136.         {
  137.                 using std::swap;
  138.                 swap(ij.i, ij.j);
  139.         }
  140.  
  141.         //now do the 2d problem in the i,j plane
  142.  
  143.         check_i = checkp.*ij.i;
  144.         check_j = checkp.*ij.j;
  145.  
  146.         auto &vcvertptr = Vertices.vcptr;
  147.         for (edge=edgemask=0;edge<nv;edge++) {
  148.                 vec2d edgevec,checkvec;
  149.                 fix64 d;
  150.  
  151.                 auto &v0 = *vcvertptr(vertex_list[facenum * 3 + edge]);
  152.                 auto &v1 = *vcvertptr(vertex_list[facenum * 3 + ((edge + 1) % nv)]);
  153.  
  154.                 edgevec.i = v1.*ij.i - v0.*ij.i;
  155.                 edgevec.j = v1.*ij.j - v0.*ij.j;
  156.  
  157.                 checkvec.i = check_i - v0.*ij.i;
  158.                 checkvec.j = check_j - v0.*ij.j;
  159.  
  160.                 d = fixmul64(checkvec.i,edgevec.j) - fixmul64(checkvec.j,edgevec.i);
  161.  
  162.                 if (d < 0)                              //we are outside of triangle
  163.                         edgemask |= (1<<edge);
  164.         }
  165.  
  166.         return edgemask;
  167.  
  168. }
  169.  
  170. //check if a sphere intersects a face
  171. __attribute_warn_unused_result
  172. static int check_sphere_to_face(const vms_vector &pnt, const vms_vector &normal, const unsigned facenum, const unsigned nv, const fix rad, const vertex_array_list_t &vertex_list)
  173. {
  174.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  175.         auto &Vertices = LevelSharedVertexState.get_vertices();
  176.         const auto checkp = pnt;
  177.         uint edgemask;
  178.  
  179.         //now do 2d check to see if point is in side
  180.  
  181.         edgemask = check_point_to_face(pnt, normal, facenum, nv, vertex_list);
  182.  
  183.         //we've gone through all the sides, are we inside?
  184.  
  185.         if (edgemask == 0)
  186.                 return IT_FACE;
  187.         else {
  188.                 vms_vector edgevec;            //this time, real 3d vectors
  189.                 vms_vector closest_point;
  190.                 int itype;
  191.                 int edgenum;
  192.  
  193.                 //get verts for edge we're behind
  194.  
  195.                 for (edgenum=0;!(edgemask&1);(edgemask>>=1),edgenum++);
  196.  
  197.                 auto &vcvertptr = Vertices.vcptr;
  198.                 auto &v0 = *vcvertptr(vertex_list[facenum * 3 + edgenum]);
  199.                 auto &v1 = *vcvertptr(vertex_list[facenum * 3 + ((edgenum + 1) % nv)]);
  200.  
  201.                 //check if we are touching an edge or point
  202.  
  203.                 const auto checkvec = vm_vec_sub(checkp,v0);
  204.                 const auto edgelen = vm_vec_normalized_dir(edgevec,v1,v0);
  205.                
  206.                 //find point dist from planes of ends of edge
  207.  
  208.                 const auto d = vm_vec_dot(edgevec,checkvec);
  209.                 if (d < 0)
  210.                         return IT_NONE;
  211.                 else if (d > edgelen)
  212.                         return IT_NONE;
  213.  
  214.                 if (d+rad < 0) return IT_NONE;                  //too far behind start point
  215.  
  216.                 if (d-rad > edgelen) return IT_NONE;    //too far part end point
  217.  
  218.                 //find closest point on edge to check point
  219.  
  220.                 else {
  221.                         itype = IT_EDGE;
  222.  
  223.                         //vm_vec_scale(&edgevec,d);
  224.                         //vm_vec_add(&closest_point,v0,&edgevec);
  225.  
  226.                         vm_vec_scale_add(closest_point,v0,edgevec,d);
  227.                 }
  228.  
  229.                 const auto dist = vm_vec_dist2(checkp,closest_point);
  230.                 const fix64 rad64 = rad;
  231.                 if (dist > vm_distance_squared{rad64 * rad64})
  232.                         return IT_NONE;
  233.                 return itype;
  234.         }
  235.  
  236.  
  237. }
  238.  
  239. //returns true if line intersects with face. fills in newp with intersection
  240. //point on plane, whether or not line intersects side
  241. //facenum determines which of four possible faces we have
  242. //note: the seg parm is temporary, until the face itself has a point field
  243. __attribute_warn_unused_result
  244. static int check_line_to_face(vms_vector &newp, const vms_vector &p0, const vms_vector &p1, const shared_segment &seg, const unsigned side, const unsigned facenum, const unsigned nv, const fix rad)
  245. {
  246.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  247.         auto &Vertices = LevelSharedVertexState.get_vertices();
  248.         auto &s = seg.sides[side];
  249.         const vms_vector &norm = s.normals[facenum];
  250.  
  251.         const auto v = create_abs_vertex_lists(seg, s, side);
  252.         const auto &num_faces = v.first;
  253.         const auto &vertex_list = v.second;
  254.  
  255.         //use lowest point number
  256.         unsigned vertnum;
  257.         if (num_faces==2) {
  258.                 vertnum = min(vertex_list[0],vertex_list[2]);
  259.         }
  260.         else {
  261.                 auto b = begin(vertex_list);
  262.                 vertnum = *std::min_element(b, std::next(b, 4));
  263.         }
  264.  
  265.         auto &vcvertptr = Vertices.vcptr;
  266.         auto pli = find_plane_line_intersection(newp, vcvertptr(vertnum), norm, p0, p1, rad);
  267.  
  268.         if (!pli) return IT_NONE;
  269.  
  270.         auto checkp = newp;
  271.  
  272.         //if rad != 0, project the point down onto the plane of the polygon
  273.  
  274.         if (rad!=0)
  275.                 vm_vec_scale_add2(checkp,norm,-rad);
  276.  
  277.         return check_sphere_to_face(checkp, s.normals[facenum], facenum, nv, rad, vertex_list);
  278. }
  279.  
  280. //returns the value of a determinant
  281. __attribute_warn_unused_result
  282. static fix calc_det_value(const vms_matrix *det)
  283. {
  284.         return  fixmul(det->rvec.x,fixmul(det->uvec.y,det->fvec.z)) -
  285.                                 fixmul(det->rvec.x,fixmul(det->uvec.z,det->fvec.y)) -
  286.                                 fixmul(det->rvec.y,fixmul(det->uvec.x,det->fvec.z)) +
  287.                                 fixmul(det->rvec.y,fixmul(det->uvec.z,det->fvec.x)) +
  288.                                 fixmul(det->rvec.z,fixmul(det->uvec.x,det->fvec.y)) -
  289.                                 fixmul(det->rvec.z,fixmul(det->uvec.y,det->fvec.x));
  290. }
  291.  
  292. //computes the parameters of closest approach of two lines
  293. //fill in two parameters, t0 & t1.  returns 0 if lines are parallel, else 1
  294. static int check_line_to_line(fix *t1,fix *t2,const vms_vector &p1,const vms_vector &v1,const vms_vector &p2,const vms_vector &v2)
  295. {
  296.         vms_matrix det;
  297.         fix d,cross_mag2;               //mag squared cross product
  298.  
  299.         vm_vec_cross(det.fvec,v1,v2);
  300.         cross_mag2 = vm_vec_dot(det.fvec,det.fvec);
  301.  
  302.         if (cross_mag2 == 0)
  303.                 return 0;                       //lines are parallel
  304.  
  305.         vm_vec_sub(det.rvec,p2,p1);
  306.         det.uvec = v2;
  307.         d = calc_det_value(&det);
  308.         *t1 = fixdiv(d,cross_mag2);
  309.  
  310.         det.uvec = v1;
  311.         d = calc_det_value(&det);
  312.         *t2 = fixdiv(d,cross_mag2);
  313.  
  314.         return 1;               //found point
  315. }
  316.  
  317. //this version is for when the start and end positions both poke through
  318. //the plane of a side.  In this case, we must do checks against the edge
  319. //of faces
  320. __attribute_warn_unused_result
  321. static int special_check_line_to_face(vms_vector &newp, const vms_vector &p0, const vms_vector &p1, const shared_segment &seg, const unsigned side, const unsigned facenum, const unsigned nv, const fix rad)
  322. {
  323.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  324.         auto &Vertices = LevelSharedVertexState.get_vertices();
  325.         fix edge_t=0,move_t=0,edge_t2=0,move_t2=0;
  326.         int edgenum;
  327.         auto &s = seg.sides[side];
  328.  
  329.         //calc some basic stuff
  330.  
  331.         const auto v = create_abs_vertex_lists(seg, s, side);
  332.         const auto &vertex_list = v.second;
  333.         auto move_vec = vm_vec_sub(p1,p0);
  334.  
  335.         //figure out which edge(s) to check against
  336.  
  337.         unsigned edgemask = check_point_to_face(p0, s.normals[facenum], facenum, nv, vertex_list);
  338.  
  339.         if (edgemask == 0)
  340.                 return check_line_to_face(newp,p0,p1,seg,side,facenum,nv,rad);
  341.  
  342.         for (edgenum=0;!(edgemask&1);edgemask>>=1,edgenum++);
  343.  
  344.         auto &vcvertptr = Vertices.vcptr;
  345.         auto &edge_v0 = *vcvertptr(vertex_list[facenum * 3 + edgenum]);
  346.         auto &edge_v1 = *vcvertptr(vertex_list[facenum * 3 + ((edgenum + 1) % nv)]);
  347.  
  348.         auto edge_vec = vm_vec_sub(edge_v1,edge_v0);
  349.  
  350.         //is the start point already touching the edge?
  351.  
  352.         //??
  353.  
  354.         //first, find point of closest approach of vec & edge
  355.  
  356.         const auto edge_len = vm_vec_normalize(edge_vec);
  357.         const auto move_len = vm_vec_normalize(move_vec);
  358.  
  359.         check_line_to_line(&edge_t,&move_t,edge_v0,edge_vec,p0,move_vec);
  360.  
  361.         //make sure t values are in valid range
  362.         if (move_t<0 || move_t>move_len+rad)
  363.                 return IT_NONE;
  364.  
  365.         if (move_t > move_len)
  366.                 move_t2 = move_len;
  367.         else
  368.                 move_t2 = move_t;
  369.  
  370.         if (edge_t < 0)         //saturate at points
  371.                 edge_t2 = 0;
  372.         else
  373.                 edge_t2 = edge_t;
  374.        
  375.         if (edge_t2 > edge_len)         //saturate at points
  376.                 edge_t2 = edge_len;
  377.        
  378.         //now, edge_t & move_t determine closest points.  calculate the points.
  379.  
  380.         const auto closest_point_edge = vm_vec_scale_add(edge_v0,edge_vec,edge_t2);
  381.         const auto closest_point_move = vm_vec_scale_add(p0,move_vec,move_t2);
  382.  
  383.         //find dist between closest points
  384.  
  385.         const auto closest_dist = vm_vec_dist2(closest_point_edge,closest_point_move);
  386.  
  387.         //could we hit with this dist?
  388.  
  389.         //note massive tolerance here
  390.         const vm_distance fudge_rad{(rad * 15) / 20};
  391.         if (closest_dist.d2 < fudge_rad || closest_dist < fudge_rad * fudge_rad)                //we hit.  figure out where
  392.         {
  393.  
  394.                 //now figure out where we hit
  395.  
  396.                 vm_vec_scale_add(newp,p0,move_vec,move_t-rad);
  397.  
  398.                 return IT_EDGE;
  399.  
  400.         }
  401.         else
  402.                 return IT_NONE;                 //no hit
  403.  
  404. }
  405.  
  406. //maybe this routine should just return the distance and let the caller
  407. //decide it it's close enough to hit
  408. //determine if and where a vector intersects with a sphere
  409. //vector defined by p0,p1
  410. //returns dist if intersects, and fills in intp
  411. //else returns 0
  412. __attribute_warn_unused_result
  413. static vm_distance_squared check_vector_to_sphere_1(vms_vector &intp,const vms_vector &p0,const vms_vector &p1,const vms_vector &sphere_pos,fix sphere_rad)
  414. {
  415.         vms_vector dn;
  416.  
  417.         //this routine could be optimized if it's taking too much time!
  418.  
  419.         const auto d = vm_vec_sub(p1,p0);
  420.         const auto w = vm_vec_sub(sphere_pos,p0);
  421.  
  422.         const auto mag_d = vm_vec_copy_normalize(dn,d);
  423.  
  424.         if (mag_d == 0) {
  425.                 const auto int_dist = vm_vec_mag2(w);
  426.                 intp = p0;
  427.                 if (int_dist.d2 < sphere_rad)
  428.                         return int_dist;
  429.                 const fix64 sphere_rad64 = sphere_rad;
  430.                 if (int_dist < vm_distance_squared{sphere_rad64 * sphere_rad64})
  431.                         return int_dist;
  432.                 return vm_distance_squared::minimum_value();
  433.         }
  434.  
  435.         const fix w_dist = vm_vec_dot(dn,w);
  436.  
  437.         if (w_dist < 0)         //moving away from object
  438.                 return vm_distance_squared::minimum_value();
  439.  
  440.         if (w_dist > mag_d+sphere_rad)
  441.                 return vm_distance_squared::minimum_value();            //cannot hit
  442.  
  443.         const auto closest_point = vm_vec_scale_add(p0,dn,w_dist);
  444.  
  445.         const auto dist2 = vm_vec_dist2(closest_point,sphere_pos);
  446.         const fix64 sphere_rad64 = sphere_rad;
  447.         const vm_distance_squared sphere_rad_squared{sphere_rad64 * sphere_rad64};
  448.         if (dist2 < sphere_rad_squared)
  449.         {
  450.                 const fix64 delta_squared = static_cast<fix64>(sphere_rad_squared) - static_cast<fix64>(dist2);
  451.                 const fix delta = static_cast<fix>(delta_squared >> 16);
  452.                 const auto shorten = fix_sqrt(delta);
  453.                 const auto int_dist = w_dist-shorten;
  454.  
  455.                 if (int_dist > mag_d || int_dist < 0) //past one or the other end of vector, which means we're inside
  456.                 {
  457.                         //past one or the other end of vector, which means we're inside? WRONG! Either you're inside OR you didn't quite make it!
  458.                         if (vm_vec_dist2(p0, sphere_pos) < sphere_rad_squared)
  459.                         {
  460.                                 intp = p0; //don't move at all
  461.                                 return vm_distance_squared{static_cast<fix64>(1)}; // note that we do not calculate a valid collision point. This is up to collision handling.
  462.                         } else {
  463.                                 return vm_distance_squared::minimum_value();
  464.                         }
  465.                 }
  466.  
  467.                 vm_vec_scale_add(intp,p0,dn,int_dist);         //calc intersection point
  468.                 return vm_distance_squared{static_cast<fix64>(int_dist) * int_dist};
  469.         }
  470.         else
  471.                 return vm_distance_squared::minimum_value();
  472. }
  473.  
  474. /*
  475. //$$fix get_sphere_int_dist(vms_vector *w,fix dist,fix rad);
  476. //$$
  477. //$$#pragma aux get_sphere_int_dist parm [esi] [ebx] [ecx] value [eax] modify exact [eax ebx ecx edx] = \
  478. //$$    "mov eax,ebx"           \
  479. //$$    "imul eax"                      \
  480. //$$                                                    \
  481. //$$    "mov ebx,eax"           \
  482. //$$   "mov eax,ecx"            \
  483. //$$    "mov ecx,edx"           \
  484. //$$                                                    \
  485. //$$    "imul eax"                      \
  486. //$$                                                    \
  487. //$$    "sub eax,ebx"           \
  488. //$$    "sbb edx,ecx"           \
  489. //$$                                                    \
  490. //$$    "call quad_sqrt"        \
  491. //$$                                                    \
  492. //$$    "push eax"                      \
  493. //$$                                                    \
  494. //$$    "push ebx"                      \
  495. //$$    "push ecx"                      \
  496. //$$                                                    \
  497. //$$    "mov eax,[esi]" \
  498. //$$    "imul eax"                      \
  499. //$$    "mov ebx,eax"           \
  500. //$$    "mov ecx,edx"           \
  501. //$$    "mov eax,4[esi]"        \
  502. //$$    "imul eax"                      \
  503. //$$    "add ebx,eax"           \
  504. //$$    "adc ecx,edx"           \
  505. //$$    "mov eax,8[esi]"        \
  506. //$$    "imul eax"                      \
  507. //$$    "add eax,ebx"           \
  508. //$$    "adc edx,ecx"           \
  509. //$$                                                    \
  510. //$$    "pop ecx"                       \
  511. //$$    "pop ebx"                       \
  512. //$$                                                    \
  513. //$$    "sub eax,ebx"           \
  514. //$$    "sbb edx,ecx"           \
  515. //$$                                                    \
  516. //$$    "call quad_sqrt"        \
  517. //$$                                                    \
  518. //$$    "pop ebx"                       \
  519. //$$    "sub eax,ebx";
  520. //$$
  521. //$$
  522. //$$//determine if and where a vector intersects with a sphere
  523. //$$//vector defined by p0,p1
  524. //$$//returns dist if intersects, and fills in intp. if no intersect, return 0
  525. //$$fix check_vector_to_sphere_2(vms_vector *intp,vms_vector *p0,vms_vector *p1,vms_vector *sphere_pos,fix sphere_rad)
  526. //$${
  527. //$$    vms_vector d,w,c;
  528. //$$    fix mag_d,dist,mag_c,mag_w;
  529. //$$    vms_vector wn,dn;
  530. //$$
  531. //$$    vm_vec_sub(&d,p1,p0);
  532. //$$    vm_vec_sub(&w,sphere_pos,p0);
  533. //$$
  534. //$$    //wn = w; mag_w = vm_vec_normalize(&wn);
  535. //$$    //dn = d; mag_d = vm_vec_normalize(&dn);
  536. //$$
  537. //$$    mag_w = vm_vec_copy_normalize(&wn,&w);
  538. //$$    mag_d = vm_vec_copy_normalize(&dn,&d);
  539. //$$
  540. //$$    //vm_vec_cross(&c,&w,&d);
  541. //$$    vm_vec_cross(&c,&wn,&dn);
  542. //$$
  543. //$$    mag_c = vm_vec_mag(&c);
  544. //$$    //mag_d = vm_vec_mag(&d);
  545. //$$
  546. //$$    //dist = fixdiv(mag_c,mag_d);
  547. //$$
  548. //$$dist = fixmul(mag_c,mag_w);
  549. //$$
  550. //$$    if (dist < sphere_rad) {        //we intersect.  find point of intersection
  551. //$$            fix int_dist;                   //length of vector to intersection point
  552. //$$            fix k;                                  //portion of p0p1 we want
  553. //$$//@@                fix dist2,rad2,shorten,mag_w2;
  554. //$$
  555. //$$//@@                mag_w2 = vm_vec_dot(&w,&w);     //the square of the magnitude
  556. //$$//@@                //WHAT ABOUT OVERFLOW???
  557. //$$//@@                dist2 = fixmul(dist,dist);
  558. //$$//@@                rad2 = fixmul(sphere_rad,sphere_rad);
  559. //$$//@@                shorten = fix_sqrt(rad2 - dist2);
  560. //$$//@@                int_dist = fix_sqrt(mag_w2 - dist2) - shorten;
  561. //$$
  562. //$$            int_dist = get_sphere_int_dist(&w,dist,sphere_rad);
  563. //$$
  564. //$$if (labs(int_dist) > mag_d) //I don't know why this would happen
  565. //$$    if (int_dist > 0)
  566. //$$            k = f1_0;
  567. //$$    else
  568. //$$            k = -f1_0;
  569. //$$else
  570. //$$            k = fixdiv(int_dist,mag_d);
  571. //$$
  572. //$$//          vm_vec_scale(&d,k);                     //vec from p0 to intersection point
  573. //$$//          vm_vec_add(intp,p0,&d);         //intersection point
  574. //$$            vm_vec_scale_add(intp,p0,&d,k); //calc new intersection point
  575. //$$
  576. //$$            return int_dist;
  577. //$$    }
  578. //$$    else
  579. //$$            return 0;       //no intersection
  580. //$$}
  581. */
  582.  
  583.  
  584.  
  585. //determine if a vector intersects with an object
  586. //if no intersects, returns 0, else fills in intp and returns dist
  587. __attribute_warn_unused_result
  588. static vm_distance_squared check_vector_to_object(vms_vector &intp, const vms_vector &p0, const vms_vector &p1, const fix rad, const object_base &obj, const object &otherobj)
  589. {
  590.         fix size = obj.size;
  591.  
  592.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  593.         if (obj.type == OBJ_ROBOT && Robot_info[get_robot_id(obj)].attack_type)
  594.                 size = (size*3)/4;
  595.  
  596.         //if obj is player, and bumping into other player or a weapon of another coop player, reduce radius
  597.         if (obj.type == OBJ_PLAYER &&
  598.                         (otherobj.type == OBJ_PLAYER ||
  599.                         ((Game_mode & GM_MULTI_COOP) && otherobj.type == OBJ_WEAPON && otherobj.ctype.laser_info.parent_type == OBJ_PLAYER)))
  600.                 size = size/2;
  601.  
  602.         return check_vector_to_sphere_1(intp, p0, p1, obj.pos, size+rad);
  603. }
  604.  
  605.  
  606. namespace {
  607.  
  608. #define MAX_SEGS_VISITED 100
  609. struct fvi_segment_visit_count_t
  610. {
  611.         unsigned count = 0;
  612. };
  613.  
  614. struct fvi_segments_visited_t : public fvi_segment_visit_count_t, public visited_segment_bitarray_t
  615. {
  616. };
  617.  
  618. //these vars are used to pass vars from fvi_sub() to find_vector_intersection()
  619.  
  620. }
  621.  
  622. namespace dsx {
  623. static int fvi_sub(vms_vector &intp, segnum_t &ints, const vms_vector &p0, const vcsegptridx_t startseg, const vms_vector &p1, fix rad, const icobjptridx_t thisobjnum, const std::pair<const vcobjidx_t *, const vcobjidx_t *> ignore_obj_list, int flags, fvi_info::segment_array_t &seglist, segnum_t entry_seg, fvi_segments_visited_t &visited, unsigned &fvi_hit_side, icsegidx_t &fvi_hit_side_seg, unsigned &fvi_nest_count, icsegidx_t &fvi_hit_pt_seg, const vms_vector *&wall_norm, icobjidx_t &fvi_hit_object);
  624.  
  625. //What the hell is fvi_hit_seg for???
  626.  
  627. //Find out if a vector intersects with anything.
  628. //Fills in hit_data, an fvi_info structure (see header file).
  629. //Parms:
  630. //  p0 & startseg       describe the start of the vector
  631. //  p1                                  the end of the vector
  632. //  rad                                         the radius of the cylinder
  633. //  thisobjnum          used to prevent an object with colliding with itself
  634. //  ingore_obj                  ignore collisions with this object
  635. //  check_obj_flag      determines whether collisions with objects are checked
  636. //Returns the hit_data->hit_type
  637. }
  638. int find_vector_intersection(const fvi_query &fq, fvi_info &hit_data)
  639. {
  640.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  641.         auto &Objects = LevelUniqueObjectState.Objects;
  642.         auto &Vertices = LevelSharedVertexState.get_vertices();
  643.         auto &imobjptridx = Objects.imptridx;
  644.         int hit_type;
  645.         segnum_t hit_seg2;
  646.         vms_vector hit_pnt;
  647.  
  648.         icobjidx_t fvi_hit_object = object_none;        // object number of object hit in last find_vector_intersection call.
  649.  
  650.         //check to make sure start point is in seg its supposed to be in
  651.         //Assert(check_point_in_seg(p0,startseg,0).centermask==0);      //start point not in seg
  652.  
  653.         // invalid segnum, so say there is no hit.
  654.         if(fq.startseg > Highest_segment_index)
  655.         {
  656.                 Assert(fq.startseg <= Highest_segment_index);
  657.                 hit_data.hit_type = HIT_BAD_P0;
  658.                 hit_data.hit_pnt = *fq.p0;
  659.                 hit_data.hit_seg = hit_data.hit_side = hit_data.hit_object = 0;
  660.                 hit_data.hit_side_seg = segment_none;
  661.  
  662.                 return hit_data.hit_type;
  663.         }
  664.  
  665.         auto &vcvertptr = Vertices.vcptr;
  666.         // Viewer is not in segment as claimed, so say there is no hit.
  667.         if(!(get_seg_masks(vcvertptr, *fq.p0, vcsegptr(fq.startseg), 0).centermask == 0))
  668.         {
  669.  
  670.                 hit_data.hit_type = HIT_BAD_P0;
  671.                 hit_data.hit_pnt = *fq.p0;
  672.                 hit_data.hit_seg = fq.startseg;
  673.                 hit_data.hit_side = hit_data.hit_object = 0;
  674.                 hit_data.hit_side_seg = segment_none;
  675.  
  676.                 return hit_data.hit_type;
  677.         }
  678.  
  679.         fvi_segments_visited_t visited;
  680.         visited[fq.startseg] = true;
  681.  
  682.         unsigned fvi_hit_side = ~0u;
  683.         icsegidx_t fvi_hit_side_seg = segment_none;     // what seg the hitside is in
  684.         unsigned fvi_nest_count = 0;
  685.  
  686.         icsegidx_t fvi_hit_pt_seg = segment_none;               // what segment the hit point is in
  687.         hit_seg2 = segment_none;
  688.  
  689.         const vms_vector *wall_norm = nullptr;  //surface normal of hit wall
  690.         hit_type = fvi_sub(hit_pnt, hit_seg2, *fq.p0, vcsegptridx(fq.startseg), *fq.p1, fq.rad, imobjptridx(fq.thisobjnum), fq.ignore_obj_list, fq.flags, hit_data.seglist, segment_exit, visited, fvi_hit_side, fvi_hit_side_seg, fvi_nest_count, fvi_hit_pt_seg, wall_norm, fvi_hit_object);
  691.         segnum_t hit_seg;
  692.         if (hit_seg2 != segment_none && !get_seg_masks(vcvertptr, hit_pnt, vcsegptr(hit_seg2), 0).centermask)
  693.                 hit_seg = hit_seg2;
  694.         else
  695.                 hit_seg = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, hit_pnt, imsegptridx(fq.startseg));
  696.  
  697. //MATT: TAKE OUT THIS HACK AND FIX THE BUGS!
  698.         if (hit_type == HIT_WALL && hit_seg==segment_none)
  699.                 if (fvi_hit_pt_seg != segment_none && get_seg_masks(vcvertptr, hit_pnt, vcsegptr(fvi_hit_pt_seg), 0).centermask == 0)
  700.                         hit_seg = fvi_hit_pt_seg;
  701.  
  702.         if (hit_seg == segment_none) {
  703.                 int new_hit_type;
  704.                 segnum_t new_hit_seg2=segment_none;
  705.                 vms_vector new_hit_pnt;
  706.  
  707.                 //because of code that deal with object with non-zero radius has
  708.                 //problems, try using zero radius and see if we hit a wall
  709.  
  710.                 new_hit_type = fvi_sub(new_hit_pnt, new_hit_seg2, *fq.p0, vcsegptridx(fq.startseg), *fq.p1, 0, imobjptridx(fq.thisobjnum), fq.ignore_obj_list, fq.flags, hit_data.seglist, segment_exit, visited, fvi_hit_side, fvi_hit_side_seg, fvi_nest_count, fvi_hit_pt_seg, wall_norm, fvi_hit_object);
  711.                 (void)new_hit_type; // FIXME! This should become hit_type, right?
  712.  
  713.                 if (new_hit_seg2 != segment_none) {
  714.                         hit_seg = new_hit_seg2;
  715.                         hit_pnt = new_hit_pnt;
  716.                 }
  717.         }
  718.  
  719.  
  720.         if (hit_seg!=segment_none && (fq.flags & FQ_GET_SEGLIST))
  721.         {
  722.                 fvi_info::segment_array_t::iterator i = hit_data.seglist.find(hit_seg), e = hit_data.seglist.end();
  723.                 if (i != e)
  724.                         hit_data.seglist.erase(++i);
  725.                 else if (hit_data.seglist.size() < hit_data.seglist.max_size())
  726.                         hit_data.seglist.emplace_back(hit_seg);
  727.         }
  728.  
  729. //I'm sorry to say that sometimes the seglist isn't correct.  I did my
  730. //best.  Really.
  731.  
  732.  
  733. //{     //verify hit list
  734. //
  735. //      int i,ch;
  736. //
  737. //      Assert(hit_data->seglist[0] == startseg);
  738. //
  739. //      for (i=0;i<hit_data->n_segs-1;i++) {
  740. //              for (ch=0;ch<6;ch++)
  741. //                      if (Segments[hit_data->seglist[i]].children[ch] == hit_data->seglist[i+1])
  742. //                              break;
  743. //              Assert(ch<6);
  744. //      }
  745. //
  746. //      Assert(hit_data->seglist[hit_data->n_segs-1] == hit_seg);
  747. //}
  748.        
  749.  
  750. //MATT: PUT THESE ASSERTS BACK IN AND FIX THE BUGS!
  751. //!!    Assert(hit_seg!=-1);
  752. //!!    Assert(!((hit_type==HIT_WALL) && (hit_seg == -1)));
  753.         //When this assert happens, get Matt.  Matt:  Look at hit_seg2 &
  754.         //fvi_hit_seg.  At least one of these should be set.  Why didn't
  755.         //find_new_seg() find something?
  756.  
  757. //      Assert(fvi_hit_seg==-1 || fvi_hit_seg == hit_seg);
  758.  
  759.         Assert(!(hit_type==HIT_OBJECT && fvi_hit_object==object_none));
  760.  
  761.         hit_data.hit_type               = hit_type;
  762.         hit_data.hit_pnt                = hit_pnt;
  763.         hit_data.hit_seg                = hit_seg;
  764.         hit_data.hit_side               = fvi_hit_side; //looks at global
  765.         hit_data.hit_side_seg   = fvi_hit_side_seg;     //looks at global
  766.         hit_data.hit_object             = fvi_hit_object;       //looks at global
  767.         if (wall_norm)
  768.                 hit_data.hit_wallnorm = *wall_norm;
  769.         else
  770.         {
  771.                 hit_data.hit_wallnorm = {};
  772.                 DXX_MAKE_VAR_UNDEFINED(hit_data.hit_wallnorm);
  773.         }
  774.  
  775. //      if(hit_seg != -1 && get_seg_masks(&hit_data->hit_pnt, hit_data->hit_seg, 0, __FILE__, __LINE__).centermask != 0)
  776. //              Int3();
  777.  
  778.         return hit_type;
  779.  
  780. }
  781.  
  782. __attribute_warn_unused_result
  783. static bool obj_in_list(const vcobjidx_t objnum, const std::pair<const vcobjidx_t *, const vcobjidx_t *> obj_list)
  784. {
  785.         if (unlikely(!obj_list.first))
  786.                 return false;
  787.         return std::find(obj_list.first, obj_list.second, objnum) != obj_list.second;
  788. }
  789.  
  790. namespace dsx {
  791. static int check_trans_wall(const vms_vector &pnt,vcsegptridx_t seg,int sidenum,int facenum);
  792. }
  793.  
  794. static void append_segments(fvi_info::segment_array_t &dst, const fvi_info::segment_array_t &src)
  795. {
  796.         /* Avoid overflow.  Original code had n_segs < MAX_SEGS_VISITED-1,
  797.          * so leave an extra slot on min.
  798.          */
  799.         const size_t scount = src.size(), dcount = dst.size(), count = std::min(scount, dst.max_size() - dcount - 1);
  800.         std::copy(src.begin(), src.begin() + count, std::back_inserter(dst));
  801. }
  802.  
  803. namespace dsx {
  804. static int fvi_sub(vms_vector &intp, segnum_t &ints, const vms_vector &p0, const vcsegptridx_t startseg, const vms_vector &p1, fix rad, icobjptridx_t thisobjnum, const std::pair<const vcobjidx_t *, const vcobjidx_t *> ignore_obj_list, int flags, fvi_info::segment_array_t &seglist, segnum_t entry_seg, fvi_segments_visited_t &visited, unsigned &fvi_hit_side, icsegidx_t &fvi_hit_side_seg, unsigned &fvi_nest_count, icsegidx_t &fvi_hit_pt_seg, const vms_vector *&wall_norm, icobjidx_t &fvi_hit_object)
  805. {
  806.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  807.         auto &Objects = LevelUniqueObjectState.Objects;
  808.         auto &Vertices = LevelSharedVertexState.get_vertices();
  809.         auto &vcobjptridx = Objects.vcptridx;
  810.         int startmask,endmask;  //mask of faces
  811.         //@@int sidemask;                               //mask of sides - can be on back of face but not side
  812.         int centermask;                 //where the center point is
  813.         vms_vector closest_hit_point{};         //where we hit
  814.         auto closest_d = vm_distance_squared::maximum_value();                                  //distance to hit point
  815.         int hit_type=HIT_NONE;                                                  //what sort of hit
  816.         segnum_t hit_seg=segment_none;
  817.         segnum_t hit_none_seg=segment_none;
  818.         fvi_info::segment_array_t hit_none_seglist;
  819.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  820.  
  821.         seglist.clear();
  822.         if (flags&FQ_GET_SEGLIST)
  823.                 seglist.emplace_back(startseg);
  824.  
  825.         const unsigned cur_nest_level = fvi_nest_count;
  826.         fvi_nest_count++;
  827.  
  828.         //first, see if vector hit any objects in this segment
  829.         if (flags & FQ_CHECK_OBJS)
  830.         {
  831.                 const auto &collision = CollisionResult[likely(thisobjnum != object_none) ? thisobjnum->type : 0];
  832.                 range_for (const auto objnum, objects_in(*startseg, vcobjptridx, vcsegptr))
  833.                 {
  834.                         if (objnum->flags & OF_SHOULD_BE_DEAD)
  835.                                 continue;
  836.                         if (thisobjnum != object_none)
  837.                         {
  838.                                 if (thisobjnum == objnum)
  839.                                         continue;
  840.                                 if (laser_are_related(objnum, thisobjnum))
  841.                                         continue;
  842.                                 if (collision[objnum->type] == RESULT_NOTHING)
  843.                                         continue;
  844.                         }
  845.                         if (obj_in_list(objnum, ignore_obj_list))
  846.                                 continue;
  847.                         int fudged_rad = rad;
  848.  
  849. #if defined(DXX_BUILD_DESCENT_II)
  850.                         //      If this is a powerup, don't do collision if flag FQ_IGNORE_POWERUPS is set
  851.                         if (objnum->type == OBJ_POWERUP)
  852.                                 if (flags & FQ_IGNORE_POWERUPS)
  853.                                         continue;
  854. #endif
  855.  
  856.                         //      If this is a robot:robot collision, only do it if both of them have attack_type != 0 (eg, green guy)
  857.                         if (thisobjnum->type == OBJ_ROBOT)
  858.                                 if (objnum->type == OBJ_ROBOT)
  859. #if defined(DXX_BUILD_DESCENT_I)
  860.                                         if (!(Robot_info[get_robot_id(objnum)].attack_type && Robot_info[get_robot_id(thisobjnum)].attack_type))
  861. #endif
  862.                                         // -- MK: 11/18/95, 4claws glomming together...this is easy.  -- if (!(Robot_info[Objects[objnum].id].attack_type && Robot_info[Objects[thisobjnum].id].attack_type))
  863.                                                 continue;
  864.  
  865.                         if (thisobjnum->type == OBJ_ROBOT && Robot_info[get_robot_id(thisobjnum)].attack_type)
  866.                                 fudged_rad = (rad*3)/4;
  867.  
  868.                         //if obj is player, and bumping into other player or a weapon of another coop player, reduce radius
  869.                         if (thisobjnum->type == OBJ_PLAYER &&
  870.                                         ((objnum->type == OBJ_PLAYER) ||
  871.                                         ((Game_mode&GM_MULTI_COOP) &&  objnum->type == OBJ_WEAPON && objnum->ctype.laser_info.parent_type == OBJ_PLAYER)))
  872.                                 fudged_rad = rad/2;     //(rad*3)/4;
  873.  
  874.                         vms_vector hit_point;
  875.                         const auto &&d = check_vector_to_object(hit_point,p0,p1,fudged_rad,objnum, thisobjnum);
  876.  
  877.                         if (d)          //we have intersection
  878.                                 if (d < closest_d) {
  879.                                         fvi_hit_object = objnum;
  880.                                         Assert(fvi_hit_object!=object_none);
  881.                                         closest_d = d;
  882.                                         closest_hit_point = hit_point;
  883.                                         hit_type=HIT_OBJECT;
  884.                                 }
  885.                 }
  886.         }
  887.  
  888.         if (thisobjnum != object_none && CollisionResult[thisobjnum->type][OBJ_WALL] == RESULT_NOTHING)
  889.                 rad = 0;                //HACK - ignore when edges hit walls
  890.  
  891.         //now, check segment walls
  892.  
  893.         auto &vcvertptr = Vertices.vcptr;
  894.         startmask = get_seg_masks(vcvertptr, p0, startseg, rad).facemask;
  895.  
  896.         const auto &&masks = get_seg_masks(vcvertptr, p1, startseg, rad);    //on back of which faces?
  897.         endmask = masks.facemask;
  898.         //@@sidemask = masks.sidemask;
  899.         centermask = masks.centermask;
  900.  
  901.         if (centermask==0) hit_none_seg = startseg;
  902.  
  903.         if (endmask != 0) {                             //on the back of at least one face
  904.  
  905.                 int side,bit,face;
  906.  
  907.                 //for each face we are on the back of, check if intersected
  908.  
  909.                 for (side=0,bit=1;side<6 && endmask>=bit;side++) {
  910.                         const unsigned nv = get_side_is_quad(startseg->shared_segment::sides[side]) ? 4 : 3;
  911.                         // commented out by mk on 02/13/94:: if ((num_faces=seg->sides[side].num_faces)==0) num_faces=1;
  912.  
  913.                         for (face=0;face<2;face++,bit<<=1) {
  914.  
  915.                                 if (endmask & bit) {            //on the back of this face
  916.                                         int face_hit_type;      //in what way did we hit the face?
  917.  
  918.  
  919.                                         if (startseg->children[side] == entry_seg)
  920.                                                 continue;               //don't go back through entry side
  921.  
  922.                                         //did we go through this wall/door?
  923.  
  924.                                         vms_vector hit_point;
  925.                                         if (startmask & bit)            //start was also though.  Do extra check
  926.                                                 face_hit_type = special_check_line_to_face(hit_point,
  927.                                                                                 p0,p1,startseg,side,
  928.                                                                                 face,
  929.                                                                                 nv,rad);
  930.                                         else
  931.                                                 //NOTE LINK TO ABOVE!!
  932.                                                 face_hit_type = check_line_to_face(hit_point,
  933.                                                                                 p0,p1,startseg,side,
  934.                                                                                 face,
  935.                                                                                 nv,rad);
  936.  
  937.        
  938.                                         if (face_hit_type) {            //through this wall/door
  939.                                                 auto &Walls = LevelUniqueWallSubsystemState.Walls;
  940.                                                 auto &vcwallptr = Walls.vcptr;
  941.                                                 auto wid_flag = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, startseg, side);
  942.  
  943.                                                 //if what we have hit is a door, check the adjoining seg
  944.  
  945.                                                 if (thisobjnum == get_local_player().objnum && cheats.ghostphysics)
  946.                                                 {
  947.                                                         if (IS_CHILD(startseg->children[side]))
  948.                                                                 wid_flag |= WID_FLY_FLAG;
  949.                                                 }
  950.  
  951.                                                 if ((wid_flag & WID_FLY_FLAG) ||
  952.                                                         (
  953. #if defined(DXX_BUILD_DESCENT_I)
  954.                                                                 (wid_flag == WID_TRANSPARENT_WALL) &&
  955. #elif defined(DXX_BUILD_DESCENT_II)
  956.                                                                 ((wid_flag & WID_RENDER_FLAG) && (wid_flag & WID_RENDPAST_FLAG)) &&
  957. #endif
  958.                                                                 ((flags & FQ_TRANSWALL) || (flags & FQ_TRANSPOINT && check_trans_wall(hit_point,startseg,side,face))))) {
  959.  
  960.                                                         segnum_t newsegnum,sub_hit_seg;
  961.                                                         vms_vector sub_hit_point;
  962.                                                         int sub_hit_type;
  963.                                                         const auto save_wall_norm = wall_norm;
  964.                                                         auto save_hit_objnum = fvi_hit_object;
  965.  
  966.                                                         //do the check recursively on the next seg.
  967.  
  968.                                                         newsegnum = startseg->children[side];
  969.  
  970.                                                         if (!visited[newsegnum]) {                //haven't visited here yet
  971.                                                                 visited[newsegnum] = true;
  972.                                                                 ++ visited.count;
  973.  
  974.                                                                 if (visited.count >= MAX_SEGS_VISITED)
  975.                                                                         goto quit_looking;              //we've looked a long time, so give up
  976.  
  977.                                                                 fvi_info::segment_array_t temp_seglist;
  978.                                                                 sub_hit_type = fvi_sub(sub_hit_point, sub_hit_seg, p0, startseg.absolute_sibling(newsegnum), p1, rad, thisobjnum, ignore_obj_list, flags, temp_seglist, startseg, visited, fvi_hit_side, fvi_hit_side_seg, fvi_nest_count, fvi_hit_pt_seg, wall_norm, fvi_hit_object);
  979.  
  980.                                                                 if (sub_hit_type != HIT_NONE) {
  981.  
  982.                                                                         const auto d = vm_vec_dist2(sub_hit_point,p0);
  983.  
  984.                                                                         if (d < closest_d) {
  985.  
  986.                                                                                 closest_d = d;
  987.                                                                                 closest_hit_point = sub_hit_point;
  988.                                                                                 hit_type = sub_hit_type;
  989.                                                                                 if (sub_hit_seg!=segment_none) hit_seg = sub_hit_seg;
  990.  
  991.                                                                                 //copy seglist
  992.                                                                                 if (flags&FQ_GET_SEGLIST) {
  993.                                                                                         append_segments(seglist, temp_seglist);
  994.                                                                                 }
  995.                                                                         }
  996.                                                                         else {
  997.                                                                                 wall_norm = save_wall_norm;     //global could be trashed
  998.                                                                                 fvi_hit_object = save_hit_objnum;
  999.                                                                         }
  1000.  
  1001.                                                                 }
  1002.                                                                 else {
  1003.                                                                         wall_norm = save_wall_norm;     //global could be trashed
  1004.                                                                         if (sub_hit_seg!=segment_none) hit_none_seg = sub_hit_seg;
  1005.                                                                         //copy seglist
  1006.                                                                         if (flags&FQ_GET_SEGLIST) {
  1007.                                                                                 hit_none_seglist = temp_seglist;
  1008.                                                                         }
  1009.                                                                 }
  1010.                                                         }
  1011.                                                 }
  1012.                                                 else {          //a wall
  1013.                                                                                                                                
  1014.                                                                 //is this the closest hit?
  1015.        
  1016.                                                                 const auto d = vm_vec_dist2(hit_point,p0);
  1017.        
  1018.                                                                 if (d < closest_d) {
  1019.                                                                         closest_d = d;
  1020.                                                                         closest_hit_point = hit_point;
  1021.                                                                         hit_type = HIT_WALL;
  1022.                                                                         wall_norm = &startseg->shared_segment::sides[side].normals[face];
  1023.                                                                         if (get_seg_masks(vcvertptr, hit_point, startseg, rad).centermask == 0)
  1024.                                                                                 hit_seg = startseg;             //hit in this segment
  1025.                                                                         else
  1026.                                                                                 fvi_hit_pt_seg = startseg;
  1027.  
  1028.                                                                         fvi_hit_side =  side;
  1029.                                                                         fvi_hit_side_seg = startseg;
  1030.  
  1031.                                                                 }
  1032.                                                 }
  1033.                                         }
  1034.                                 }
  1035.                         }
  1036.                 }
  1037.         }
  1038.  
  1039. //      Assert(centermask==0 || hit_seg!=startseg);
  1040.  
  1041. //      Assert(sidemask==0);            //Error("Didn't find side we went though");
  1042.  
  1043. quit_looking:
  1044.         ;
  1045.  
  1046.         if (hit_type == HIT_NONE) {     //didn't hit anything, return end point
  1047.                 intp = p1;
  1048.                 ints = hit_none_seg;
  1049.                 //MATT: MUST FIX THIS!!!!
  1050.                 //Assert(!centermask);
  1051.  
  1052.                 if (hit_none_seg!=segment_none) {                       ///(centermask == 0)
  1053.                         if (flags&FQ_GET_SEGLIST)
  1054.                                 //copy seglist
  1055.                                 append_segments(seglist, hit_none_seglist);
  1056.                 }
  1057.                 else
  1058.                         if (cur_nest_level!=0)
  1059.                                 seglist.clear();
  1060.  
  1061.         }
  1062.         else {
  1063.                 intp = closest_hit_point;
  1064.                 if (hit_seg==segment_none)
  1065.                         if (fvi_hit_pt_seg != segment_none)
  1066.                                 ints = fvi_hit_pt_seg;
  1067.                         else
  1068.                                 ints = hit_none_seg;
  1069.                 else
  1070.                         ints = hit_seg;
  1071.         }
  1072.  
  1073.         Assert(!(hit_type==HIT_OBJECT && fvi_hit_object==object_none));
  1074.  
  1075.         return hit_type;
  1076.  
  1077. }
  1078. }
  1079.  
  1080. /*
  1081. //--unused-- //compute the magnitude of a 2d vector
  1082. //--unused-- fix mag2d(vec2d *v);
  1083. //--unused-- #pragma aux mag2d parm [esi] value [eax] modify exact [eax ebx ecx edx] = \
  1084. //--unused--    "mov    eax,[esi]"              \
  1085. //--unused--    "imul   eax"                            \
  1086. //--unused--    "mov    ebx,eax"                        \
  1087. //--unused--    "mov    ecx,edx"                        \
  1088. //--unused--    "mov    eax,4[esi]"             \
  1089. //--unused--    "imul   eax"                            \
  1090. //--unused--    "add    eax,ebx"                        \
  1091. //--unused--    "adc    edx,ecx"                        \
  1092. //--unused--    "call   quad_sqrt";
  1093. */
  1094.  
  1095. //--unused-- //returns mag
  1096. //--unused-- fix normalize_2d(vec2d *v)
  1097. //--unused-- {
  1098. //--unused--    fix mag;
  1099. //--unused--
  1100. //--unused--    mag = mag2d(v);
  1101. //--unused--
  1102. //--unused--    v->i = fixdiv(v->i,mag);
  1103. //--unused--    v->j = fixdiv(v->j,mag);
  1104. //--unused--
  1105. //--unused--    return mag;
  1106. //--unused-- }
  1107.  
  1108. #include "textures.h"
  1109. #include "texmerge.h"
  1110.  
  1111. #define cross(v0,v1) (fixmul((v0)->i,(v1)->j) - fixmul((v0)->j,(v1)->i))
  1112.  
  1113. //finds the uv coords of the given point on the given seg & side
  1114. //fills in u & v. if l is non-NULL fills it in also
  1115. namespace dsx {
  1116. fvi_hitpoint find_hitpoint_uv(const vms_vector &pnt, const cscusegment seg, const uint_fast32_t sidenum, const uint_fast32_t facenum)
  1117. {
  1118.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1119.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1120.         auto &side = seg.s.sides[sidenum];
  1121.         fix k0,k1;
  1122.         int i;
  1123.  
  1124.         //do lasers pass through illusory walls?
  1125.  
  1126.         //when do I return 0 & 1 for non-transparent walls?
  1127.  
  1128.         const auto &&vn = create_all_vertnum_lists(seg, side, sidenum);
  1129.  
  1130.         //now the hard work.
  1131.  
  1132.         //1. find what plane to project this wall onto to make it a 2d case
  1133.  
  1134.         const auto &normal_array = side.normals[facenum];
  1135.         auto fmax = [](const vms_vector &v, fix vms_vector::*a, fix vms_vector::*b) {
  1136.                 return abs(v.*a) > abs(v.*b) ? a : b;
  1137.         };
  1138.         const auto biggest = fmax(normal_array, &vms_vector::z, fmax(normal_array, &vms_vector::y, &vms_vector::x));
  1139.         const auto ii = (biggest == &vms_vector::x) ? &vms_vector::y : &vms_vector::x;
  1140.         const auto jj = (biggest == &vms_vector::z) ? &vms_vector::y : &vms_vector::z;
  1141.  
  1142.         //2. compute u,v of intersection point
  1143.  
  1144.         //vec from 1 -> 0
  1145.         auto &vcvertptr = Vertices.vcptr;
  1146.         auto &vf1 = *vcvertptr(vn[facenum * 3 + 1].vertex);
  1147.         const vec2d p1{vf1.*ii, vf1.*jj};
  1148.  
  1149.         auto &vf0 = *vcvertptr(vn[facenum * 3 + 0].vertex);
  1150.         const vec2d vec0{vf0.*ii - p1.i, vf0.*jj - p1.j};
  1151.         //vec from 1 -> 2
  1152.         auto &vf2 = *vcvertptr(vn[facenum * 3 + 2].vertex);
  1153.         const vec2d vec1{vf2.*ii - p1.i, vf2.*jj - p1.j};
  1154.  
  1155.         //vec from 1 -> checkpoint
  1156.         const vec2d checkp{pnt.*ii, pnt.*jj};
  1157.  
  1158.         //@@checkv.i = checkp.i - p1.i;
  1159.         //@@checkv.j = checkp.j - p1.j;
  1160.  
  1161.         k1 = -fixdiv(cross(&checkp,&vec0) + cross(&vec0,&p1),cross(&vec0,&vec1));
  1162. #if defined(DXX_BUILD_DESCENT_I)
  1163.         if (vec0.i)
  1164. #elif defined(DXX_BUILD_DESCENT_II)
  1165.         if (abs(vec0.i) > abs(vec0.j))
  1166. #endif
  1167.                 k0 = fixdiv(fixmul(-k1,vec1.i) + checkp.i - p1.i,vec0.i);
  1168.         else
  1169.                 k0 = fixdiv(fixmul(-k1,vec1.j) + checkp.j - p1.j,vec0.j);
  1170.  
  1171.         std::array<uvl, 3> uvls;
  1172.         auto &uside = seg.u.sides[sidenum];
  1173.         for (i=0;i<3;i++)
  1174.                 uvls[i] = uside.uvls[vn[facenum * 3 + i].vertnum];
  1175.  
  1176.         auto p = [&uvls, k0, k1](fix uvl::*pmf) {
  1177.                 return uvls[1].*pmf + fixmul(k0,uvls[0].*pmf - uvls[1].*pmf) + fixmul(k1,uvls[2].*pmf - uvls[1].*pmf);
  1178.         };
  1179.         return {
  1180.                 p(&uvl::u),
  1181.                 p(&uvl::v)
  1182.         };
  1183. }
  1184.  
  1185. //check if a particular point on a wall is a transparent pixel
  1186. //returns 1 if can pass though the wall, else 0
  1187. int check_trans_wall(const vms_vector &pnt,const vcsegptridx_t seg,int sidenum,int facenum)
  1188. {
  1189.         auto &side = seg->unique_segment::sides[sidenum];
  1190.         int bmx,bmy;
  1191.  
  1192. #if defined(DXX_BUILD_DESCENT_I)
  1193. #ifndef NDEBUG
  1194.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1195.         auto &vcwallptr = Walls.vcptr;
  1196.         assert(WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, sidenum) == WID_TRANSPARENT_WALL);
  1197. #endif
  1198. #endif
  1199.  
  1200.         const auto hitpoint = find_hitpoint_uv(pnt,seg,sidenum,facenum);        //      Don't compute light value.
  1201.         auto &u = hitpoint.u;
  1202.         auto &v = hitpoint.v;
  1203.  
  1204.         const auto tmap_num = side.tmap_num;
  1205.         const grs_bitmap &rbm = (side.tmap_num2 != 0) ? texmerge_get_cached_bitmap(tmap_num, side.tmap_num2 ) :
  1206.                 GameBitmaps[Textures[PIGGY_PAGE_IN(Textures[tmap_num]), tmap_num].index];
  1207.         const auto bm = rle_expand_texture(rbm);
  1208.  
  1209.         bmx = static_cast<unsigned>(f2i(u*bm->bm_w)) % bm->bm_w;
  1210.         bmy = static_cast<unsigned>(f2i(v*bm->bm_h)) % bm->bm_h;
  1211.  
  1212. //note: the line above had -v, but that was wrong, so I changed it.  if
  1213. //something doesn't work, and you want to make it negative again, you
  1214. //should figure out what's going on.
  1215.  
  1216. #if defined(DXX_BUILD_DESCENT_I)
  1217.         return (gr_gpixel (*bm, bmx, bmy) == TRANSPARENCY_COLOR);
  1218. #elif defined(DXX_BUILD_DESCENT_II)
  1219.         return (bm->bm_data[bmy*bm->bm_w+bmx] == TRANSPARENCY_COLOR);
  1220. #endif
  1221. }
  1222. }
  1223.  
  1224. //new function for Mike
  1225. //note: n_segs_visited must be set to zero before this is called
  1226. static sphere_intersects_wall_result sphere_intersects_wall(fvcvertptr &vcvertptr, const vms_vector &pnt, const vcsegptridx_t seg, const fix rad, fvi_segments_visited_t &visited)
  1227. {
  1228.         int facemask;
  1229.         visited[seg] = true;
  1230.         ++visited.count;
  1231.  
  1232.         facemask = get_seg_masks(vcvertptr, pnt, seg, rad).facemask;
  1233.  
  1234.         if (facemask != 0) {                            //on the back of at least one face
  1235.  
  1236.                 int bit,face;
  1237.                 uint_fast32_t side;
  1238.  
  1239.                 //for each face we are on the back of, check if intersected
  1240.  
  1241.                 for (side=0,bit=1;side<6 && facemask>=bit;side++) {
  1242.  
  1243.                         for (face=0;face<2;face++,bit<<=1) {
  1244.  
  1245.                                 if (facemask & bit) {            //on the back of this face
  1246.                                         int face_hit_type;      //in what way did we hit the face?
  1247.  
  1248.                                         //did we go through this wall/door?
  1249.                                         auto &sidep = seg->shared_segment::sides[side];
  1250.                                         const auto v = create_abs_vertex_lists(seg, sidep, side);
  1251.                                         const auto &num_faces = v.first;
  1252.                                         const auto &vertex_list = v.second;
  1253.  
  1254.                                         face_hit_type = check_sphere_to_face(pnt, sidep.normals[face],
  1255.                                                                                 face,((num_faces==1)?4:3),rad,vertex_list);
  1256.  
  1257.                                         if (face_hit_type) {            //through this wall/door
  1258.                                                 //if what we have hit is a door, check the adjoining seg
  1259.  
  1260.                                                 auto child = seg->children[side];
  1261.  
  1262.                                                 if (!IS_CHILD(child))
  1263.                                                 {
  1264.                                                         return {static_cast<const segment *>(seg), side};
  1265.                                                 }
  1266.                                                 else if (!visited[child]) {                //haven't visited here yet
  1267.                                                         const auto &&r = sphere_intersects_wall(vcvertptr, pnt, seg.absolute_sibling(child), rad, visited);
  1268.                                                         if (r.seg)
  1269.                                                                 return r;
  1270.                                                 }
  1271.                                         }
  1272.                                 }
  1273.                         }
  1274.                 }
  1275.         }
  1276.         return {};
  1277. }
  1278.  
  1279. sphere_intersects_wall_result sphere_intersects_wall(fvcvertptr &vcvertptr, const vms_vector &pnt, const vcsegptridx_t seg, const fix rad)
  1280. {
  1281.         fvi_segments_visited_t visited;
  1282.         return sphere_intersects_wall(vcvertptr, pnt, seg, rad, visited);
  1283. }
  1284.