Subversion Repositories Games.Descent

Rev

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

  1. /*
  2.  * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
  3.  * It is copyright by its individual contributors, as recorded in the
  4.  * project's Git history.  See COPYING.txt at the top level for license
  5.  * terms and a link to the Git history.
  6.  */
  7.  
  8. #include "3d.h"
  9. #include "globvars.h"
  10. #include "clipper.h"
  11. #include "dxxerror.h"
  12.  
  13. #include "compiler-range_for.h"
  14. #include <stdexcept>
  15.  
  16. namespace dcx {
  17.  
  18. #if !DXX_USE_OGL
  19. temporary_points_t::temporary_points_t() :
  20.         free_point_num(0)
  21. {
  22.         auto p = &temp_points.front();
  23.         range_for (auto &f, free_points)
  24.                 f = p++;
  25. }
  26.  
  27. static g3s_point &get_temp_point(temporary_points_t &t)
  28. {
  29.         if (t.free_point_num >= t.free_points.size())
  30.                 throw std::out_of_range("not enough free points");
  31.         auto &p = *t.free_points[t.free_point_num++];
  32.         p.p3_flags = PF_TEMP_POINT;
  33.         return p;
  34. }
  35.  
  36. void temporary_points_t::free_temp_point(g3s_point *p)
  37. {
  38.         if (!(p->p3_flags & PF_TEMP_POINT))
  39.                 throw std::invalid_argument("freeing non-temporary point");
  40.         if (--free_point_num >= free_points.size())
  41.                 throw std::out_of_range("too many free points");
  42.         free_points[free_point_num] = p;
  43.         p->p3_flags &= ~PF_TEMP_POINT;
  44. }
  45.  
  46. //clips an edge against one plane.
  47. static g3s_point &clip_edge(int plane_flag,g3s_point *on_pnt,g3s_point *off_pnt, temporary_points_t &tp)
  48. {
  49.         fix psx_ratio;
  50.         fix a,b,kn,kd;
  51.  
  52.         //compute clipping value k = (xs-zs) / (xs-xe-zs+ze)
  53.         //use x or y as appropriate, and negate x/y value as appropriate
  54.  
  55.         if (plane_flag & (CC_OFF_RIGHT | CC_OFF_LEFT)) {
  56.                 a = on_pnt->p3_x;
  57.                 b = off_pnt->p3_x;
  58.         }
  59.         else {
  60.                 a = on_pnt->p3_y;
  61.                 b = off_pnt->p3_y;
  62.         }
  63.  
  64.         if (plane_flag & (CC_OFF_LEFT | CC_OFF_BOT)) {
  65.                 a = -a;
  66.                 b = -b;
  67.         }
  68.  
  69.         kn = a - on_pnt->p3_z;                                          //xs-zs
  70.         kd = kn - b + off_pnt->p3_z;                            //xs-zs-xe+ze
  71.  
  72.         auto &tmp = get_temp_point(tp);
  73.  
  74.         psx_ratio = fixdiv( kn, kd );
  75.         tmp.p3_x = on_pnt->p3_x + fixmul( (off_pnt->p3_x-on_pnt->p3_x), psx_ratio);
  76.         tmp.p3_y = on_pnt->p3_y + fixmul( (off_pnt->p3_y-on_pnt->p3_y), psx_ratio);
  77.  
  78.         if (plane_flag & (CC_OFF_TOP|CC_OFF_BOT))
  79.                 tmp.p3_z = tmp.p3_y;
  80.         else
  81.                 tmp.p3_z = tmp.p3_x;
  82.  
  83.         if (plane_flag & (CC_OFF_LEFT|CC_OFF_BOT))
  84.                 tmp.p3_z = -tmp.p3_z;
  85.  
  86.         if (on_pnt->p3_flags & PF_UVS) {
  87. // PSX_HACK!!!!
  88. //              tmp.p3_u = on_pnt->p3_u + fixmuldiv(off_pnt->p3_u-on_pnt->p3_u,kn,kd);
  89. //              tmp.p3_v = on_pnt->p3_v + fixmuldiv(off_pnt->p3_v-on_pnt->p3_v,kn,kd);
  90.                 tmp.p3_u = on_pnt->p3_u + fixmul((off_pnt->p3_u-on_pnt->p3_u), psx_ratio);
  91.                 tmp.p3_v = on_pnt->p3_v + fixmul((off_pnt->p3_v-on_pnt->p3_v), psx_ratio);
  92.  
  93.                 tmp.p3_flags |= PF_UVS;
  94.         }
  95.  
  96.         if (on_pnt->p3_flags & PF_LS) {
  97. // PSX_HACK
  98. //              tmp.p3_r = on_pnt->p3_r + fixmuldiv(off_pnt->p3_r-on_pnt->p3_r,kn,kd);
  99. //              tmp.p3_g = on_pnt->p3_g + fixmuldiv(off_pnt->p3_g-on_pnt->p3_g,kn,kd);
  100. //              tmp.p3_b = on_pnt->p3_b + fixmuldiv(off_pnt->p3_b-on_pnt->p3_b,kn,kd);
  101.                 tmp.p3_l = on_pnt->p3_l + fixmul((off_pnt->p3_l-on_pnt->p3_l), psx_ratio);
  102.                 tmp.p3_flags |= PF_LS;
  103.         }
  104.         g3_code_point(tmp);
  105.         return tmp;
  106. }
  107.  
  108. //clips a line to the viewing pyramid.
  109. void clip_line(g3s_point *&p0,g3s_point *&p1,const uint_fast8_t codes_or, temporary_points_t &tp)
  110. {
  111.         //might have these left over
  112.         p0->p3_flags &= ~(PF_UVS|PF_LS);
  113.         p1->p3_flags &= ~(PF_UVS|PF_LS);
  114.  
  115.         for (int plane_flag=1;plane_flag<16;plane_flag<<=1)
  116.                 if (codes_or & plane_flag) {
  117.  
  118.                         if (p0->p3_codes & plane_flag)
  119.                                 std::swap(p0, p1);
  120.                         const auto old_p1 = std::exchange(p1, &clip_edge(plane_flag,p0,p1,tp));
  121.                         if (old_p1->p3_flags & PF_TEMP_POINT)
  122.                                 tp.free_temp_point(old_p1);
  123.                 }
  124. }
  125.  
  126. static int clip_plane(int plane_flag,polygon_clip_points &src,polygon_clip_points &dest,int *nv,g3s_codes *cc, temporary_points_t &tp)
  127. {
  128.         //copy first two verts to end
  129.         src[*nv] = src[0];
  130.         src[*nv+1] = src[1];
  131.  
  132.         cc->uand = 0xff; cc->uor = 0;
  133.  
  134.         uint_fast32_t j = 0;
  135.         for (int i=1;i<=*nv;i++) {
  136.  
  137.                 if (src[i]->p3_codes & plane_flag) {                            //cur point off?
  138.  
  139.                         if (! (src[i-1]->p3_codes & plane_flag)) {      //prev not off?
  140.  
  141.                                 dest[j] = &clip_edge(plane_flag,src[i-1],src[i],tp);
  142.                                 cc->uor  |= dest[j]->p3_codes;
  143.                                 cc->uand &= dest[j]->p3_codes;
  144.                                 ++j;
  145.                         }
  146.  
  147.                         if (! (src[i+1]->p3_codes & plane_flag)) {
  148.  
  149.                                 dest[j] = &clip_edge(plane_flag,src[i+1],src[i],tp);
  150.                                 cc->uor  |= dest[j]->p3_codes;
  151.                                 cc->uand &= dest[j]->p3_codes;
  152.                                 ++j;
  153.                         }
  154.  
  155.                         //see if must free discarded point
  156.  
  157.                         if (src[i]->p3_flags & PF_TEMP_POINT)
  158.                                 tp.free_temp_point(src[i]);
  159.                 }
  160.                 else {                  //cur not off, copy to dest buffer
  161.  
  162.                         dest[j++] = src[i];
  163.  
  164.                         cc->uor  |= src[i]->p3_codes;
  165.                         cc->uand &= src[i]->p3_codes;
  166.                 }
  167.         }
  168.         return j;
  169. }
  170.  
  171. const polygon_clip_points &clip_polygon(polygon_clip_points &rsrc,polygon_clip_points &rdest,int *nv,g3s_codes *cc, temporary_points_t &tp)
  172. {
  173.         polygon_clip_points *src = &rsrc, *dest = &rdest;
  174.         for (int plane_flag=1;plane_flag<16;plane_flag<<=1)
  175.  
  176.                 if (cc->uor & plane_flag) {
  177.  
  178.                         *nv = clip_plane(plane_flag,*src,*dest,nv,cc,tp);
  179.  
  180.                         if (cc->uand)           //clipped away
  181.                                 return *dest;
  182.  
  183.                         std::swap(src, dest);
  184.                 }
  185.  
  186.         return *src;            //we swapped after we copied
  187. }
  188. #endif
  189.  
  190. }
  191.