home *** CD-ROM | disk | FTP | other *** search
/ WADS of WADS / WadsOfWads.1994.zip / OTHER / DOOMBSP / BUILDBSP.M next >
Text File  |  1994-04-06  |  10KB  |  528 lines

  1. #import "doombsp.h"
  2.  
  3. // I assume that a grid 8 is used for the maps, so a point will be considered
  4. // on a line if it is within 8 pixels of it.  The accounts for floating error.
  5.  
  6. int        cuts;            // number of new lines generated by BSP process
  7.  
  8. /*
  9. ==================
  10. =
  11. = DivlineFromWorldline
  12. =
  13. ==================
  14. */
  15.  
  16. void    DivlineFromWorldline (divline_t *d, line_t *w)
  17. {
  18.     d->pt = w->p1;
  19.     d->dx = w->p2.x - w->p1.x;
  20.     d->dy = w->p2.y - w->p1.y;
  21. }
  22.  
  23. /*
  24. ==================
  25. =
  26. = PointOnSide
  27. =
  28. = Returns side 0 (front), 1 (back), or -1 (colinear)
  29. ==================
  30. */
  31.  
  32. int    PointOnSide (NXPoint *p, divline_t *l)
  33. {
  34.     float    dx,dy;
  35.     float    left, right;
  36.     float    a,b,c,d;
  37.     
  38.     if (!l->dx)
  39.     {
  40.         if (p->x > l->pt.x-2 && p->x < l->pt.x+2)
  41.             return -1;
  42.         if (p->x < l->pt.x)
  43.             return l->dy > 0;
  44.         return l->dy < 0;
  45.     }
  46.     if (!l->dy)
  47.     {
  48.         if (p->y > l->pt.y-2 && p->y < l->pt.y+2)
  49.             return -1;
  50.         if (p->y < l->pt.y)
  51.             return l->dx < 0;
  52.         return l->dx > 0;
  53.     }
  54.  
  55.     dx = l->pt.x - p->x;
  56.     dy = l->pt.y - p->y;
  57.     a = l->dx*l->dx + l->dy*l->dy;
  58.     b = 2*(l->dx*dx + l->dy*dy);
  59.     c = dx*dx+dy*dy - 2*2;        // 2 unit radius
  60.     d = b*b - 4*a*c;
  61.     if (d>0)
  62.         return -1;        // within four pixels of line
  63.     
  64.     
  65.     dx = p->x - l->pt.x;
  66.     dy = p->y - l->pt.y;
  67.     
  68.     left = l->dy * dx;
  69.     right = dy * l->dx;
  70.     
  71.     if ( fabs (left-right) < 0.5 )    // allow slop
  72.         return -1;        // on line
  73.     if (right < left)
  74.         return 0;        // front side
  75.     return 1;            // back side
  76. }
  77.  
  78. /*
  79. =============
  80. =
  81. = sign
  82. =
  83. = Returns -1, 0, or 1, based on the input sign
  84. =
  85. ==============
  86. */
  87.  
  88. int sign (float i)
  89. {
  90.     if (i<0)
  91.         return -1;
  92.     else if (i>0)
  93.         return 1;
  94.     return 0;
  95. }
  96.  
  97. /*
  98. ==================
  99. =
  100. = LineOnSide
  101. =
  102. = Returns side 0 / 1, or -2 if line must be split
  103. = If the line is colinear, it will be placed on the front side if
  104. = it is going the same direction as the dividing line
  105. ==================
  106. */
  107.  
  108. boolean    LineOnSide (line_t *wl, divline_t *bl)
  109. {
  110.     int        s1,s2;
  111.     float    dx, dy;
  112.     
  113.     s1 = PointOnSide (&wl->p1, bl);
  114.     s2 = PointOnSide (&wl->p2, bl);
  115.  
  116.     if (s1 == s2)
  117.     {
  118.         if (s1 == -1)
  119.         {    // colinear, so see if the directions are the same
  120.             dx = wl->p2.x - wl->p1.x;
  121.             dy = wl->p2.y - wl->p1.y;
  122.             if (sign(dx) == sign (bl->dx) && sign(dy) == sign(bl->dy) )
  123.                 return 0;
  124.             return 1;
  125.         }
  126.         return s1;
  127.     }
  128.     if (s1 == -1)
  129.         return s2;
  130.     if (s2 == -1)
  131.         return s1;
  132.         
  133.     return -2;
  134. }
  135.  
  136. /*
  137. ===============
  138. =
  139. = InterceptVector
  140. =
  141. = Returns the fractional intercept point along first vector
  142. ===============
  143. */
  144.  
  145. float InterceptVector (divline_t *v2, divline_t *v1)
  146. {
  147. #if 0
  148.  
  149. v1.x + f1*v1.xs = v2.x + f2*v2.xs    (parametric x coordinates)
  150. f1*v1.xs = v2.x - v1.x + f2*v2.xs
  151. f1 = (v2.x - v1.x +f2*v2.xs) / v1.xs
  152.  
  153. v1.y + f1*v1.ys = v2.y + f2*v2.ys    (parametric y coordinates)
  154. f1 = (v2.y - v1.y + f2*v2.ys) / v1.ys
  155.  
  156. f1 = (v2.x - v1.x +f2*v2.xs) / v1.xs = (v2.y - v1.y + f2*v2.ys) / v1.ys
  157. v1.ys*v2.x - v1.ys*v1.x + v1.ys*v2.xs*f2 = v1.xs*v2.y - v1.xs*v1.y + v1.xs*v2.ys*f2
  158. (v1.ys*v2.xs - v1.xs*v2.ys)*f2 = -v1.ys*v2.x + v1.ys*v1.x + v1.xs*v2.y - v1.xs*v1.y
  159.                             = v1.ys*(v1.x-v2.x) + v1.xs*(v2.y-v1.y)
  160.                             
  161. f2 = (v1.ys*(v1.x-v2.x) + v1.xs*(v2.y-v1.y)) / (v1.ys*v2.xs - v1.xs*v2.ys)
  162. #endif
  163.  
  164.     float    frac, num, den;
  165.     
  166.     den = v1->dy*v2->dx - v1->dx*v2->dy;
  167.     if (den == 0)
  168.         Error ("InterceptVector: parallel");
  169.     num = (v1->pt.x - v2->pt.x)*v1->dy + (v2->pt.y - v1->pt.y)*v1->dx;
  170.     frac = num / den;
  171.  
  172.     if (frac <= 0.0 || frac >= 1.0)
  173.         Error ("InterceptVector: intersection outside line");
  174.         
  175.     return frac;
  176. }
  177.  
  178.  
  179. /*
  180. ==================
  181. =
  182. = CutLine
  183. =
  184. = Truncates the given worldline to the front side of the divline
  185. = and returns the cut off back side in a newly allocated worldline
  186. ==================
  187. */
  188.  
  189. float round (float x)
  190. {
  191.     if (x>0)
  192.     {
  193.         if (x - (int)x < 0.1)
  194.             return (int)x;
  195.         else if (x - (int)x > 0.9)
  196.             return (int)x+1;
  197.         else
  198.             return x;
  199.     }
  200.     
  201.     if ((int)x - x < 0.1)
  202.         return (int)x;
  203.     else if ((int)x - x > 0.9)
  204.         return  (int)x - 1;
  205.     return x;
  206. }
  207.  
  208. line_t    *CutLine (line_t *wl, divline_t *bl)
  209. {
  210.     int            side;
  211.     line_t        *new_p;
  212.     divline_t    wld;
  213.     float        frac;
  214.     NXPoint        intr;
  215.     int            offset;
  216.     
  217.     cuts++;
  218.     DivlineFromWorldline (&wld, wl);
  219.     new_p = malloc (sizeof(line_t));
  220.     memset (new_p,0,sizeof(*new_p));
  221.     *new_p = *wl; 
  222.     
  223.     frac = InterceptVector (&wld, bl);
  224.     intr.x = wld.pt.x + round(wld.dx*frac);
  225.     intr.y = wld.pt.y + round(wld.dy*frac);
  226.     offset = wl->offset + round(frac*sqrt(wld.dx*wld.dx+wld.dy*wld.dy));
  227.     side = PointOnSide (&wl->p1, bl);
  228.     if (side == 0)
  229.     {    // line starts on front side
  230.         wl->p2 = intr;
  231.         new_p->p1 = intr;
  232.         new_p->offset = offset;
  233.     }
  234.     else
  235.     {    // line starts on back side
  236.         wl->p1 = intr;
  237.         wl->offset = offset;
  238.         new_p->p2 = intr;
  239.     }
  240.     
  241.     return new_p;
  242. }
  243.  
  244.  
  245. /*
  246. ================
  247. =
  248. = EvaluateSplit
  249. =
  250. = Returns a number grading the quality of a split along the givent line
  251. = for the current list of lines.  Evaluation is halted as soon as it is
  252. = determined that a better split already exists
  253. = A split is good if it divides the lines evenly without cutting many lines
  254. = A horizontal or vertical split is better than a sloping split
  255. =
  256. = The LOWER the returned value, the better.  If the split line does not divide
  257. = any of the lines at all, MAXINT will be returned
  258. ================
  259. */
  260.  
  261. int EvaluateSplit (id lines_i, line_t *spliton, int bestgrade)
  262. {
  263.     int                i,c,side;
  264.     line_t            *line_p;
  265.     divline_t        divline;
  266.     int                frontcount, backcount, max, new;
  267.     int                grade;
  268.     worldline_t        *wl;
  269.     
  270.     wl = [linestore_i elementAt: spliton->linedef];
  271. #if 0
  272.     if (wl->special == BSPSLIDEENDSPECIAL)
  273.         return MAXINT;    // NEVER split on this, because it moves
  274. #endif
  275.     DivlineFromWorldline (&divline, spliton);
  276.     
  277.     frontcount = backcount = 0;
  278.     c = [lines_i count];
  279.     grade = 0;
  280.         
  281.     for (i=0 ; i<c ; i++)
  282.     {
  283.         line_p = [lines_i elementAt:i];
  284.         if (line_p == spliton)
  285.             side = 0;
  286.         else
  287.             side = LineOnSide (line_p, &divline);
  288.         switch (side)
  289.         {
  290.         case 0:
  291.             frontcount++;
  292.             break;
  293.         case 1:
  294.             backcount++;
  295.             break;
  296.         case -2:
  297.             wl = [linestore_i elementAt: line_p->linedef];
  298. #if 0
  299.             if (wl->special == BSPSLIDESIDESPECIAL)
  300.                 return MAXINT;    // NEVER split this line, because it slides
  301. #endif
  302.             frontcount++;
  303.             backcount++;
  304.             break;
  305.         }
  306.         
  307.         max = MAX(frontcount,backcount);
  308.         new = (frontcount+backcount) - c;
  309.         grade = max+new*8;
  310.         if (grade > bestgrade)
  311.             return grade;        // might as well stop now
  312.     }
  313.     
  314.     if (frontcount == 0 || backcount == 0)
  315.         return MAXINT;            // line does not partition at all
  316.         
  317.     return grade;
  318. }
  319.  
  320.  
  321. /*
  322. ================
  323. =
  324. = ExecuteSplit
  325. =
  326. = Actually splits the line list as EvaluateLines predicted
  327. ================
  328. */
  329.  
  330. void ExecuteSplit (id lines_i, line_t *spliton
  331.     , id frontlist_i, id backlist_i)
  332. {
  333.     int                i,c,side;
  334.     line_t            *line_p, *newline_p;
  335.     divline_t        divline;
  336.     
  337.     DivlineFromWorldline (&divline, spliton);
  338.     DrawDivLine (&divline);
  339.     
  340.     c = [lines_i count];
  341.         
  342.     for (i=0 ; i<c ; i++)
  343.     {
  344.         line_p = [lines_i elementAt:i];
  345.         if (line_p == spliton)
  346.             side = 0;
  347.         else
  348.             side = LineOnSide (line_p, &divline);
  349.         switch (side)
  350.         {
  351.         case 0:
  352.             [frontlist_i addElement: line_p];
  353.             break;
  354.         case 1:
  355.             [backlist_i addElement: line_p];
  356.             break;
  357.         case -2:
  358.             newline_p = CutLine (line_p, &divline);
  359.             [frontlist_i addElement: line_p];
  360.             [backlist_i addElement: newline_p];
  361.             break;
  362.         default:
  363.             Error ("ExecuteSplit: bad side");
  364.         }
  365.     }
  366. }
  367.  
  368.  
  369. /*
  370. ================
  371. =
  372. = BSPList
  373. =
  374. = Takes a storage of lines and recursively partitions the list
  375. = Returns a bspnode_t
  376. ================
  377. */
  378.  
  379. float    gray = NX_WHITE;
  380.  
  381. bspnode_t *BSPList (id lines_i)
  382. {
  383.     id                frontlist_i, backlist_i;
  384.     int                i,c, step;
  385.     line_t            *line_p, *bestline_p;
  386.     int                v, bestv;
  387.     bspnode_t        *node_p;
  388.     
  389.     if (draw)
  390.         PSsetgray (gray);
  391.     gray = 1.0 - gray;
  392.     DrawLineStore (lines_i);
  393.     
  394.     node_p = malloc (sizeof(*node_p));
  395.     memset (node_p, 0, sizeof(*node_p));
  396.  
  397. //
  398. // find the best line to partition on 
  399. //
  400.     c = [lines_i count];
  401.     bestv = MAXINT;    
  402.     bestline_p = NULL;
  403.     step = (c/40)+1;        // set this to 1 for an exhaustive search
  404. research:
  405.     for (i=0 ; i<c ; i+=step)
  406.     {
  407.         line_p = [lines_i elementAt:i];
  408.         v = EvaluateSplit (lines_i, line_p, bestv);
  409.         if (v<bestv)
  410.         {
  411.             bestv = v;
  412.             bestline_p = line_p;
  413.         }
  414.     }
  415.     
  416. //
  417. // if none of the lines should be split, the remaining lines
  418. // are convex, and form a terminal node
  419. //
  420. //printf ("bestv:%i\n",bestv);
  421.  
  422.     if (bestv == MAXINT)
  423.     {
  424.         if (step > 1)
  425.         {    // possible to get here with non convex area if BSPSLIDE specials
  426.             // caused rejections
  427.             step = 1;
  428.             goto research;
  429.         }
  430.         node_p->lines_i = lines_i;
  431.         return node_p;
  432.     }
  433.     
  434. //
  435. // divide the line list into two nodes along the best split line
  436. //
  437.     DivlineFromWorldline (&node_p->divline, bestline_p);
  438.  
  439.     frontlist_i =
  440.     [[Storage alloc]
  441.         initCount:        0
  442.         elementSize:    sizeof(line_t)
  443.         description:    NULL];
  444.     backlist_i =
  445.     [[Storage alloc]
  446.         initCount:        0
  447.         elementSize:    sizeof(line_t)
  448.         description:    NULL];
  449.         
  450.     ExecuteSplit (lines_i, bestline_p, frontlist_i, backlist_i);
  451.  
  452. //
  453. // recursively divide the lists
  454. //
  455.     node_p->side[0] = BSPList (frontlist_i);
  456.     node_p->side[1] = BSPList (backlist_i);
  457.     
  458.     return node_p;
  459. }
  460.  
  461.  
  462.  
  463. /*
  464. =====================
  465. =
  466. = MakeSegs
  467. =
  468. =====================
  469. */
  470.  
  471. id segstore_i;
  472.  
  473. void MakeSegs (void)
  474. {
  475.     int                i, count;
  476.     worldline_t        *wl;
  477.     line_t            li;
  478.  
  479.     segstore_i =
  480.     [[Storage alloc]
  481.         initCount:        0
  482.         elementSize:    sizeof(line_t)
  483.         description:    NULL];
  484.     
  485.     count = [linestore_i count];
  486.     wl = [linestore_i elementAt:0];
  487.     for (i= 0 ; i<count ; i++, wl++)
  488.     {
  489.         li.p1 = wl->p1;
  490.         li.p2 = wl->p2;
  491.         li.linedef = i;
  492.         li.side = 0;
  493.         li.offset = 0;
  494.         li.grouped = false;
  495.         [segstore_i addElement: &li];
  496.         
  497.         if (wl->flags & ML_TWOSIDED)
  498.         {
  499.             li.p1 = wl->p2;
  500.             li.p2 = wl->p1;
  501.             li.linedef = i;
  502.             li.side = 1;
  503.             li.offset = 0;
  504.             li.grouped = false;
  505.             [segstore_i addElement: &li];
  506.         }
  507.     }        
  508. }
  509.  
  510.  
  511. /*
  512. =====================
  513. =
  514. = BuildBSP
  515. =
  516. =====================
  517. */
  518.  
  519. bspnode_t    *startnode;
  520.  
  521. void BuildBSP (void)
  522. {
  523.     MakeSegs ();
  524.     cuts = 0;
  525.     startnode = BSPList (segstore_i);
  526. }
  527.