1 //------------------------------------------------------------------------
2 // ANALYZE : Analyzing level structures
3 //------------------------------------------------------------------------
4 //
5 //  GL-Friendly Node Builder (C) 2000-2007 Andrew Apted
6 //
7 //  Based on 'BSP 2.3' by Colin Reed, Lee Killough and others.
8 //
9 //  This program is free software; you can redistribute it and/or
10 //  modify it under the terms of the GNU General Public License
11 //  as published by the Free Software Foundation; either version 2
12 //  of the License, or (at your option) any later version.
13 //
14 //  This program is distributed in the hope that it will be useful,
15 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //  GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20 
21 #include "system.h"
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <math.h>
29 #include <limits.h>
30 #include <assert.h>
31 
32 #include "analyze.h"
33 #include "blockmap.h"
34 #include "level.h"
35 #include "node.h"
36 #include "reject.h"
37 #include "seg.h"
38 #include "structs.h"
39 #include "util.h"
40 #include "wad.h"
41 
42 
43 #define DEBUG_WALLTIPS   0
44 #define DEBUG_POLYOBJ    0
45 #define DEBUG_WINDOW_FX  0
46 
47 #define POLY_BOX_SZ  10
48 
49 // stuff needed from level.c (this file closely related)
50 extern vertex_t  ** lev_vertices;
51 extern linedef_t ** lev_linedefs;
52 extern sidedef_t ** lev_sidedefs;
53 extern sector_t  ** lev_sectors;
54 
55 extern boolean_g lev_doing_normal;
56 
57 
58 /* ----- polyobj handling ----------------------------- */
59 
MarkPolyobjSector(sector_t * sector)60 static void MarkPolyobjSector(sector_t *sector)
61 {
62   int i;
63 
64   if (! sector)
65     return;
66 
67 # if DEBUG_POLYOBJ
68   PrintDebug("  Marking SECTOR %d\n", sector->index);
69 # endif
70 
71   /* already marked ? */
72   if (sector->has_polyobj)
73     return;
74 
75   /* mark all lines of this sector as precious, to prevent the sector
76    * from being split.
77    */
78   sector->has_polyobj = TRUE;
79 
80   for (i = 0; i < num_linedefs; i++)
81   {
82     linedef_t *L = lev_linedefs[i];
83 
84     if ((L->right && L->right->sector == sector) ||
85         (L->left && L->left->sector == sector))
86     {
87       L->is_precious = TRUE;
88     }
89   }
90 }
91 
MarkPolyobjPoint(float_g x,float_g y)92 static void MarkPolyobjPoint(float_g x, float_g y)
93 {
94   int i;
95   int inside_count = 0;
96 
97   float_g best_dist = 999999;
98   linedef_t *best_match = NULL;
99   sector_t *sector = NULL;
100 
101   float_g x1, y1;
102   float_g x2, y2;
103 
104   // -AJA- First we handle the "awkward" cases where the polyobj sits
105   //       directly on a linedef or even a vertex.  We check all lines
106   //       that intersect a small box around the spawn point.
107 
108   int bminx = (int) (x - POLY_BOX_SZ);
109   int bminy = (int) (y - POLY_BOX_SZ);
110   int bmaxx = (int) (x + POLY_BOX_SZ);
111   int bmaxy = (int) (y + POLY_BOX_SZ);
112 
113   for (i = 0; i < num_linedefs; i++)
114   {
115     linedef_t *L = lev_linedefs[i];
116 
117     if (CheckLinedefInsideBox(bminx, bminy, bmaxx, bmaxy,
118           (int) L->start->x, (int) L->start->y,
119           (int) L->end->x, (int) L->end->y))
120     {
121 #     if DEBUG_POLYOBJ
122       PrintDebug("  Touching line was %d\n", L->index);
123 #     endif
124 
125       if (L->left)
126         MarkPolyobjSector(L->left->sector);
127 
128       if (L->right)
129         MarkPolyobjSector(L->right->sector);
130 
131       inside_count++;
132     }
133   }
134 
135   if (inside_count > 0)
136     return;
137 
138   // -AJA- Algorithm is just like in DEU: we cast a line horizontally
139   //       from the given (x,y) position and find all linedefs that
140   //       intersect it, choosing the one with the closest distance.
141   //       If the point is sitting directly on a (two-sided) line,
142   //       then we mark the sectors on both sides.
143 
144   for (i = 0; i < num_linedefs; i++)
145   {
146     linedef_t *L = lev_linedefs[i];
147 
148     float_g x_cut;
149 
150     x1 = L->start->x;  y1 = L->start->y;
151     x2 = L->end->x;    y2 = L->end->y;
152 
153     /* check vertical range */
154     if (fabs(y2 - y1) < DIST_EPSILON)
155       continue;
156 
157     if ((y > (y1 + DIST_EPSILON) && y > (y2 + DIST_EPSILON)) ||
158         (y < (y1 - DIST_EPSILON) && y < (y2 - DIST_EPSILON)))
159       continue;
160 
161     x_cut = x1 + (x2 - x1) * (y - y1) / (y2 - y1) - x;
162 
163     if (fabs(x_cut) < fabs(best_dist))
164     {
165       /* found a closer linedef */
166 
167       best_match = L;
168       best_dist = x_cut;
169     }
170   }
171 
172   if (! best_match)
173   {
174     PrintWarn("Bad polyobj thing at (%1.0f,%1.0f).\n", x, y);
175     return;
176   }
177 
178   y1 = best_match->start->y;
179   y2 = best_match->end->y;
180 
181 # if DEBUG_POLYOBJ
182   PrintDebug("  Closest line was %d Y=%1.0f..%1.0f (dist=%1.1f)\n",
183       best_match->index, y1, y2, best_dist);
184 # endif
185 
186   /* sanity check: shouldn't be directly on the line */
187 # if DEBUG_POLYOBJ
188   if (fabs(best_dist) < DIST_EPSILON)
189   {
190     PrintDebug("  Polyobj FAILURE: directly on the line (%d)\n",
191         best_match->index);
192   }
193 # endif
194 
195   /* check orientation of line, to determine which side the polyobj is
196    * actually on.
197    */
198   if ((y1 > y2) == (best_dist > 0))
199     sector = best_match->right ? best_match->right->sector : NULL;
200   else
201     sector = best_match->left ? best_match->left->sector : NULL;
202 
203 # if DEBUG_POLYOBJ
204   PrintDebug("  Sector %d contains the polyobj.\n",
205       sector ? sector->index : -1);
206 # endif
207 
208   if (! sector)
209   {
210     PrintWarn("Invalid Polyobj thing at (%1.0f,%1.0f).\n", x, y);
211     return;
212   }
213 
214   MarkPolyobjSector(sector);
215 }
216 
217 //
218 // DetectPolyobjSectors
219 //
220 // Based on code courtesy of Janis Legzdinsh.
221 //
DetectPolyobjSectors(void)222 void DetectPolyobjSectors(void)
223 {
224   int i;
225   int hexen_style;
226 
227   // -JL- There's a conflict between Hexen polyobj thing types and Doom thing
228   //      types. In Doom type 3001 is for Imp and 3002 for Demon. To solve
229   //      this problem, first we are going through all lines to see if the
230   //      level has any polyobjs. If found, we also must detect what polyobj
231   //      thing types are used - Hexen ones or ZDoom ones. That's why we
232   //      are going through all things searching for ZDoom polyobj thing
233   //      types. If any found, we assume that ZDoom polyobj thing types are
234   //      used, otherwise Hexen polyobj thing types are used.
235 
236   // -JL- First go through all lines to see if level contains any polyobjs
237   for (i = 0; i < num_linedefs; i++)
238   {
239     linedef_t *L = lev_linedefs[i];
240 
241     if (L->type == HEXTYPE_POLY_START || L->type == HEXTYPE_POLY_EXPLICIT)
242       break;
243   }
244 
245   if (i == num_linedefs)
246   {
247     // -JL- No polyobjs in this level
248     return;
249   }
250 
251   // -JL- Detect what polyobj thing types are used - Hexen ones or ZDoom ones
252   hexen_style = TRUE;
253 
254   for (i = 0; i < num_things; i++)
255   {
256     thing_t *T = LookupThing(i);
257 
258     if (T->type == ZDOOM_PO_SPAWN_TYPE || T->type == ZDOOM_PO_SPAWNCRUSH_TYPE)
259     {
260       // -JL- A ZDoom style polyobj thing found
261       hexen_style = FALSE;
262       break;
263     }
264   }
265 
266 # if DEBUG_POLYOBJ
267   PrintDebug("Using %s style polyobj things\n",
268       hexen_style ? "HEXEN" : "ZDOOM");
269 # endif
270 
271   for (i = 0; i < num_things; i++)
272   {
273     thing_t *T = LookupThing(i);
274 
275     float_g x = (float_g) T->x;
276     float_g y = (float_g) T->y;
277 
278     // ignore everything except polyobj start spots
279     if (hexen_style)
280     {
281       // -JL- Hexen style polyobj things
282       if (T->type != PO_SPAWN_TYPE && T->type != PO_SPAWNCRUSH_TYPE)
283         continue;
284     }
285     else
286     {
287       // -JL- ZDoom style polyobj things
288       if (T->type != ZDOOM_PO_SPAWN_TYPE && T->type != ZDOOM_PO_SPAWNCRUSH_TYPE)
289         continue;
290     }
291 
292 #   if DEBUG_POLYOBJ
293     PrintDebug("Thing %d at (%1.0f,%1.0f) is a polyobj spawner.\n", i, x, y);
294 #   endif
295 
296     MarkPolyobjPoint(x, y);
297   }
298 }
299 
300 /* ----- analysis routines ----------------------------- */
301 
VertexCompare(const void * p1,const void * p2)302 static int VertexCompare(const void *p1, const void *p2)
303 {
304   int vert1 = ((const uint16_g *) p1)[0];
305   int vert2 = ((const uint16_g *) p2)[0];
306 
307   vertex_t *A = lev_vertices[vert1];
308   vertex_t *B = lev_vertices[vert2];
309 
310   if (vert1 == vert2)
311     return 0;
312 
313   if ((int)A->x != (int)B->x)
314     return (int)A->x - (int)B->x;
315 
316   return (int)A->y - (int)B->y;
317 }
318 
SidedefCompare(const void * p1,const void * p2)319 static int SidedefCompare(const void *p1, const void *p2)
320 {
321   int comp;
322 
323   int side1 = ((const uint16_g *) p1)[0];
324   int side2 = ((const uint16_g *) p2)[0];
325 
326   sidedef_t *A = lev_sidedefs[side1];
327   sidedef_t *B = lev_sidedefs[side2];
328 
329   if (side1 == side2)
330     return 0;
331 
332   // don't merge sidedefs on special lines
333   if (A->on_special || B->on_special)
334     return side1 - side2;
335 
336   if (A->sector != B->sector)
337   {
338     if (A->sector == NULL) return -1;
339     if (B->sector == NULL) return +1;
340 
341     return (A->sector->index - B->sector->index);
342   }
343 
344   if ((int)A->x_offset != (int)B->x_offset)
345     return A->x_offset - (int)B->x_offset;
346 
347   if ((int)A->y_offset != B->y_offset)
348     return (int)A->y_offset - (int)B->y_offset;
349 
350   // compare textures
351 
352   comp = memcmp(A->upper_tex, B->upper_tex, sizeof(A->upper_tex));
353   if (comp) return comp;
354 
355   comp = memcmp(A->lower_tex, B->lower_tex, sizeof(A->lower_tex));
356   if (comp) return comp;
357 
358   comp = memcmp(A->mid_tex, B->mid_tex, sizeof(A->mid_tex));
359   if (comp) return comp;
360 
361   // sidedefs must be the same
362   return 0;
363 }
364 
DetectDuplicateVertices(void)365 void DetectDuplicateVertices(void)
366 {
367   int i;
368   uint16_g *array = UtilCalloc(num_vertices * sizeof(uint16_g));
369 
370   DisplayTicker();
371 
372   // sort array of indices
373   for (i=0; i < num_vertices; i++)
374     array[i] = i;
375 
376   qsort(array, num_vertices, sizeof(uint16_g), VertexCompare);
377 
378   // now mark them off
379   for (i=0; i < num_vertices - 1; i++)
380   {
381     // duplicate ?
382     if (VertexCompare(array + i, array + i+1) == 0)
383     {
384       vertex_t *A = lev_vertices[array[i]];
385       vertex_t *B = lev_vertices[array[i+1]];
386 
387       // found a duplicate !
388       B->equiv = A->equiv ? A->equiv : A;
389     }
390   }
391 
392   UtilFree(array);
393 }
394 
DetectDuplicateSidedefs(void)395 void DetectDuplicateSidedefs(void)
396 {
397   int i;
398   uint16_g *array = UtilCalloc(num_sidedefs * sizeof(uint16_g));
399 
400   DisplayTicker();
401 
402   // sort array of indices
403   for (i=0; i < num_sidedefs; i++)
404     array[i] = i;
405 
406   qsort(array, num_sidedefs, sizeof(uint16_g), SidedefCompare);
407 
408   // now mark them off
409   for (i=0; i < num_sidedefs - 1; i++)
410   {
411     // duplicate ?
412     if (SidedefCompare(array + i, array + i+1) == 0)
413     {
414       sidedef_t *A = lev_sidedefs[array[i]];
415       sidedef_t *B = lev_sidedefs[array[i+1]];
416 
417       // found a duplicate !
418       B->equiv = A->equiv ? A->equiv : A;
419     }
420   }
421 
422   UtilFree(array);
423 }
424 
PruneLinedefs(void)425 void PruneLinedefs(void)
426 {
427   int i;
428   int new_num;
429 
430   DisplayTicker();
431 
432   // scan all linedefs
433   for (i=0, new_num=0; i < num_linedefs; i++)
434   {
435     linedef_t *L = lev_linedefs[i];
436 
437     // handle duplicated vertices
438     while (L->start->equiv)
439     {
440       L->start->ref_count--;
441       L->start = L->start->equiv;
442       L->start->ref_count++;
443     }
444 
445     while (L->end->equiv)
446     {
447       L->end->ref_count--;
448       L->end = L->end->equiv;
449       L->end->ref_count++;
450     }
451 
452     // handle duplicated sidedefs
453     while (L->right && L->right->equiv)
454     {
455       L->right->ref_count--;
456       L->right = L->right->equiv;
457       L->right->ref_count++;
458     }
459 
460     while (L->left && L->left->equiv)
461     {
462       L->left->ref_count--;
463       L->left = L->left->equiv;
464       L->left->ref_count++;
465     }
466 
467     // remove zero length lines
468     if (L->zero_len)
469     {
470       L->start->ref_count--;
471       L->end->ref_count--;
472 
473       UtilFree(L);
474       continue;
475     }
476 
477     L->index = new_num;
478     lev_linedefs[new_num++] = L;
479   }
480 
481   if (new_num < num_linedefs)
482   {
483     PrintVerbose("Pruned %d zero-length linedefs\n", num_linedefs - new_num);
484     num_linedefs = new_num;
485   }
486 
487   if (new_num == 0)
488     FatalError("Couldn't find any Linedefs");
489 }
490 
PruneVertices(void)491 void PruneVertices(void)
492 {
493   int i;
494   int new_num;
495   int unused = 0;
496 
497   DisplayTicker();
498 
499   // scan all vertices
500   for (i=0, new_num=0; i < num_vertices; i++)
501   {
502     vertex_t *V = lev_vertices[i];
503 
504     if (V->ref_count < 0)
505       InternalError("Vertex %d ref_count is %d", i, V->ref_count);
506 
507     if (V->ref_count == 0)
508     {
509       if (V->equiv == NULL)
510         unused++;
511 
512       UtilFree(V);
513       continue;
514     }
515 
516     V->index = new_num;
517     lev_vertices[new_num++] = V;
518   }
519 
520   if (new_num < num_vertices)
521   {
522     int dup_num = num_vertices - new_num - unused;
523 
524     if (unused > 0)
525       PrintVerbose("Pruned %d unused vertices "
526         "(this is normal if the nodes were built before)\n", unused);
527 
528     if (dup_num > 0)
529       PrintVerbose("Pruned %d duplicate vertices\n", dup_num);
530 
531     num_vertices = new_num;
532   }
533 
534   if (new_num == 0)
535     FatalError("Couldn't find any Vertices");
536 
537   num_normal_vert = num_vertices;
538 }
539 
PruneSidedefs(void)540 void PruneSidedefs(void)
541 {
542   int i;
543   int new_num;
544   int unused = 0;
545 
546   DisplayTicker();
547 
548   // scan all sidedefs
549   for (i=0, new_num=0; i < num_sidedefs; i++)
550   {
551     sidedef_t *S = lev_sidedefs[i];
552 
553     if (S->ref_count < 0)
554       InternalError("Sidedef %d ref_count is %d", i, S->ref_count);
555 
556     if (S->ref_count == 0)
557     {
558       if (S->sector)
559         S->sector->ref_count--;
560 
561       if (S->equiv == NULL)
562         unused++;
563 
564       UtilFree(S);
565       continue;
566     }
567 
568     S->index = new_num;
569     lev_sidedefs[new_num++] = S;
570   }
571 
572   if (new_num < num_sidedefs)
573   {
574     int dup_num = num_sidedefs - new_num - unused;
575 
576     if (unused > 0)
577       PrintVerbose("Pruned %d unused sidedefs\n", unused);
578 
579     if (dup_num > 0)
580       PrintVerbose("Pruned %d duplicate sidedefs\n", dup_num);
581 
582     num_sidedefs = new_num;
583   }
584 
585   if (new_num == 0)
586     FatalError("Couldn't find any Sidedefs");
587 }
588 
PruneSectors(void)589 void PruneSectors(void)
590 {
591   int i;
592   int new_num;
593 
594   DisplayTicker();
595 
596   // scan all sectors
597   for (i=0, new_num=0; i < num_sectors; i++)
598   {
599     sector_t *S = lev_sectors[i];
600 
601     if (S->ref_count < 0)
602       InternalError("Sector %d ref_count is %d", i, S->ref_count);
603 
604     if (S->ref_count == 0)
605     {
606       UtilFree(S);
607       continue;
608     }
609 
610     S->index = new_num;
611     lev_sectors[new_num++] = S;
612   }
613 
614   if (new_num < num_sectors)
615   {
616     PrintVerbose("Pruned %d unused sectors\n", num_sectors - new_num);
617     num_sectors = new_num;
618   }
619 
620   if (new_num == 0)
621     FatalError("Couldn't find any Sectors");
622 }
623 
LineVertexLowest(const linedef_t * L)624 static INLINE_G int LineVertexLowest(const linedef_t *L)
625 {
626   // returns the "lowest" vertex (normally the left-most, but if the
627   // line is vertical, then the bottom-most) => 0 for start, 1 for end.
628 
629   return ((int)L->start->x < (int)L->end->x ||
630           ((int)L->start->x == (int)L->end->x &&
631            (int)L->start->y <  (int)L->end->y)) ? 0 : 1;
632 }
633 
LineStartCompare(const void * p1,const void * p2)634 static int LineStartCompare(const void *p1, const void *p2)
635 {
636   int line1 = ((const int *) p1)[0];
637   int line2 = ((const int *) p2)[0];
638 
639   linedef_t *A = lev_linedefs[line1];
640   linedef_t *B = lev_linedefs[line2];
641 
642   vertex_t *C;
643   vertex_t *D;
644 
645   if (line1 == line2)
646     return 0;
647 
648   // determine left-most vertex of each line
649   C = LineVertexLowest(A) ? A->end : A->start;
650   D = LineVertexLowest(B) ? B->end : B->start;
651 
652   if ((int)C->x != (int)D->x)
653     return (int)C->x - (int)D->x;
654 
655   return (int)C->y - (int)D->y;
656 }
657 
LineEndCompare(const void * p1,const void * p2)658 static int LineEndCompare(const void *p1, const void *p2)
659 {
660   int line1 = ((const int *) p1)[0];
661   int line2 = ((const int *) p2)[0];
662 
663   linedef_t *A = lev_linedefs[line1];
664   linedef_t *B = lev_linedefs[line2];
665 
666   vertex_t *C;
667   vertex_t *D;
668 
669   if (line1 == line2)
670     return 0;
671 
672   // determine right-most vertex of each line
673   C = LineVertexLowest(A) ? A->start : A->end;
674   D = LineVertexLowest(B) ? B->start : B->end;
675 
676   if ((int)C->x != (int)D->x)
677     return (int)C->x - (int)D->x;
678 
679   return (int)C->y - (int)D->y;
680 }
681 
DetectOverlappingLines(void)682 void DetectOverlappingLines(void)
683 {
684   // Algorithm:
685   //   Sort all lines by left-most vertex.
686   //   Overlapping lines will then be near each other in this set.
687   //   Note: does not detect partially overlapping lines.
688 
689   int i;
690   int *array = UtilCalloc(num_linedefs * sizeof(int));
691   int count = 0;
692 
693   DisplayTicker();
694 
695   // sort array of indices
696   for (i=0; i < num_linedefs; i++)
697     array[i] = i;
698 
699   qsort(array, num_linedefs, sizeof(int), LineStartCompare);
700 
701   for (i=0; i < num_linedefs - 1; i++)
702   {
703     int j;
704 
705     for (j = i+1; j < num_linedefs; j++)
706     {
707       if (LineStartCompare(array + i, array + j) != 0)
708         break;
709 
710       if (LineEndCompare(array + i, array + j) == 0)
711       {
712         linedef_t *A = lev_linedefs[array[i]];
713         linedef_t *B = lev_linedefs[array[j]];
714 
715         // found an overlap !
716         B->overlap = A->overlap ? A->overlap : A;
717 
718         count++;
719       }
720     }
721   }
722 
723   if (count > 0)
724   {
725       PrintVerbose("Detected %d overlapped linedefs\n", count);
726   }
727 
728   UtilFree(array);
729 }
730 
CountWallTips(vertex_t * vert,int * one_sided,int * two_sided)731 static void CountWallTips(vertex_t *vert, int *one_sided, int *two_sided)
732 {
733     wall_tip_t *tip;
734 
735     *one_sided = 0;
736     *two_sided = 0;
737 
738     for (tip=vert->tip_set; tip; tip=tip->next)
739     {
740       if (!tip->left || !tip->right)
741         (*one_sided) += 1;
742       else
743         (*two_sided) += 1;
744     }
745 }
746 
TestForWindowEffect(linedef_t * L)747 void TestForWindowEffect(linedef_t *L)
748 {
749   // cast a line horizontally or vertically and see what we hit.
750   // OUCH, we have to iterate over all linedefs.
751 
752   int i;
753 
754   float_g mx = (L->start->x + L->end->x) / 2.0;
755   float_g my = (L->start->y + L->end->y) / 2.0;
756 
757   float_g dx = L->end->x - L->start->x;
758   float_g dy = L->end->y - L->start->y;
759 
760   int cast_horiz = fabs(dx) < fabs(dy) ? 1 : 0;
761 
762   float_g back_dist = 999999.0;
763   sector_t * back_open = NULL;
764   int back_line = -1;
765 
766   float_g front_dist = 999999.0;
767   sector_t * front_open = NULL;
768   int front_line = -1;
769 
770   for (i=0; i < num_linedefs; i++)
771   {
772     linedef_t *N = lev_linedefs[i];
773 
774     float_g dist;
775     boolean_g is_front;
776     sidedef_t *hit_side;
777 
778     float_g dx2, dy2;
779 
780     if (N == L || N->zero_len || N->overlap)
781       continue;
782 
783     if (cast_horiz)
784     {
785       dx2 = N->end->x - N->start->x;
786       dy2 = N->end->y - N->start->y;
787 
788       if (fabs(dy2) < DIST_EPSILON)
789         continue;
790 
791       if ((MAX(N->start->y, N->end->y) < my - DIST_EPSILON) ||
792           (MIN(N->start->y, N->end->y) > my + DIST_EPSILON))
793         continue;
794 
795       dist = (N->start->x + (my - N->start->y) * dx2 / dy2) - mx;
796 
797       is_front = ((dy > 0) == (dist > 0)) ? TRUE : FALSE;
798 
799       dist = fabs(dist);
800       if (dist < DIST_EPSILON)  // too close (overlapping lines ?)
801         continue;
802 
803       hit_side = ((dy > 0) ^ (dy2 > 0) ^ !is_front) ? N->right : N->left;
804     }
805     else  /* vert */
806     {
807       dx2 = N->end->x - N->start->x;
808       dy2 = N->end->y - N->start->y;
809 
810       if (fabs(dx2) < DIST_EPSILON)
811         continue;
812 
813       if ((MAX(N->start->x, N->end->x) < mx - DIST_EPSILON) ||
814           (MIN(N->start->x, N->end->x) > mx + DIST_EPSILON))
815         continue;
816 
817       dist = (N->start->y + (mx - N->start->x) * dy2 / dx2) - my;
818 
819       is_front = ((dx > 0) != (dist > 0)) ? TRUE : FALSE;
820 
821       dist = fabs(dist);
822 
823       hit_side = ((dx > 0) ^ (dx2 > 0) ^ !is_front) ? N->right : N->left;
824     }
825 
826     if (dist < DIST_EPSILON)  // too close (overlapping lines ?)
827       continue;
828 
829     if (is_front)
830     {
831       if (dist < front_dist)
832       {
833         front_dist = dist;
834         front_open = hit_side ? hit_side->sector : NULL;
835         front_line = i;
836       }
837     }
838     else
839     {
840       if (dist < back_dist)
841       {
842         back_dist = dist;
843         back_open = hit_side ? hit_side->sector : NULL;
844         back_line = i;
845       }
846     }
847   }
848 
849 #if DEBUG_WINDOW_FX
850   PrintDebug("back line: %d  back dist: %1.1f  back_open: %s\n",
851       back_line, back_dist, back_open ? "OPEN" : "CLOSED");
852   PrintDebug("front line: %d  front dist: %1.1f  front_open: %s\n",
853       front_line, front_dist, front_open ? "OPEN" : "CLOSED");
854 #endif
855 
856   if (back_open && front_open && L->right->sector == front_open)
857   {
858     L->window_effect = back_open;
859     PrintMiniWarn("Linedef #%d seems to be a One-Sided Window (back faces sector #%d).\n", L->index, back_open->index);
860   }
861 }
862 
DetectWindowEffects(void)863 void DetectWindowEffects(void)
864 {
865   // Algorithm:
866   //   Scan the linedef list looking for possible candidates,
867   //   checking for an odd number of one-sided linedefs connected
868   //   to a single vertex.  This idea courtesy of Graham Jackson.
869 
870   int i;
871   int one_siders;
872   int two_siders;
873 
874   for (i=0; i < num_linedefs; i++)
875   {
876     linedef_t *L = lev_linedefs[i];
877 
878     if (L->two_sided || L->zero_len || L->overlap || !L->right)
879       continue;
880 
881     CountWallTips(L->start, &one_siders, &two_siders);
882 
883     if ((one_siders % 2) == 1 && (one_siders + two_siders) > 1)
884     {
885 #if DEBUG_WINDOW_FX
886       PrintDebug("FUNNY LINE %d : start vertex %d has odd number of one-siders\n",
887           i, L->start->index);
888 #endif
889       TestForWindowEffect(L);
890       continue;
891     }
892 
893     CountWallTips(L->end, &one_siders, &two_siders);
894 
895     if ((one_siders % 2) == 1 && (one_siders + two_siders) > 1)
896     {
897 #if DEBUG_WINDOW_FX
898       PrintDebug("FUNNY LINE %d : end vertex %d has odd number of one-siders\n",
899           i, L->end->index);
900 #endif
901       TestForWindowEffect(L);
902     }
903   }
904 }
905 
906 
907 /* ----- vertex routines ------------------------------- */
908 
VertexAddWallTip(vertex_t * vert,float_g dx,float_g dy,sector_t * left,sector_t * right)909 static void VertexAddWallTip(vertex_t *vert, float_g dx, float_g dy,
910   sector_t *left, sector_t *right)
911 {
912   wall_tip_t *tip = NewWallTip();
913   wall_tip_t *after;
914 
915   tip->angle = UtilComputeAngle(dx, dy);
916   tip->left  = left;
917   tip->right = right;
918 
919   // find the correct place (order is increasing angle)
920   for (after=vert->tip_set; after && after->next; after=after->next)
921   { }
922 
923   while (after && tip->angle + ANG_EPSILON < after->angle)
924     after = after->prev;
925 
926   // link it in
927   tip->next = after ? after->next : vert->tip_set;
928   tip->prev = after;
929 
930   if (after)
931   {
932     if (after->next)
933       after->next->prev = tip;
934 
935     after->next = tip;
936   }
937   else
938   {
939     if (vert->tip_set)
940       vert->tip_set->prev = tip;
941 
942     vert->tip_set = tip;
943   }
944 }
945 
CalculateWallTips(void)946 void CalculateWallTips(void)
947 {
948   int i;
949 
950   DisplayTicker();
951 
952   for (i=0; i < num_linedefs; i++)
953   {
954     linedef_t *line = lev_linedefs[i];
955 
956     float_g x1;
957     float_g y1;
958     float_g x2;
959     float_g y2;
960 
961     sector_t *left;
962     sector_t *right;
963 
964     if (line->self_ref && cur_info->skip_self_ref)
965       continue;
966 
967     x1 = line->start->x;
968     y1 = line->start->y;
969     x2 = line->end->x;
970     y2 = line->end->y;
971 
972     left  = (line->left)  ? line->left->sector  : NULL;
973     right = (line->right) ? line->right->sector : NULL;
974 
975     VertexAddWallTip(line->start, x2-x1, y2-y1, left, right);
976     VertexAddWallTip(line->end,   x1-x2, y1-y2, right, left);
977   }
978 
979 # if DEBUG_WALLTIPS
980   for (i=0; i < num_vertices; i++)
981   {
982     vertex_t *vert = LookupVertex(i);
983     wall_tip_t *tip;
984 
985     PrintDebug("WallTips for vertex %d:\n", i);
986 
987     for (tip=vert->tip_set; tip; tip=tip->next)
988     {
989       PrintDebug("  Angle=%1.1f left=%d right=%d\n", tip->angle,
990         tip->left ? tip->left->index : -1,
991         tip->right ? tip->right->index : -1);
992     }
993   }
994 # endif
995 }
996 
997 //
998 // NewVertexFromSplitSeg
999 //
NewVertexFromSplitSeg(seg_t * seg,float_g x,float_g y)1000 vertex_t *NewVertexFromSplitSeg(seg_t *seg, float_g x, float_g y)
1001 {
1002   vertex_t *vert = NewVertex();
1003 
1004   vert->x = x;
1005   vert->y = y;
1006 
1007   vert->ref_count = seg->partner ? 4 : 2;
1008 
1009   if (lev_doing_normal && cur_info->spec_version == 1)
1010   {
1011     vert->index = num_normal_vert;
1012     num_normal_vert++;
1013   }
1014   else
1015   {
1016     vert->index = num_gl_vert | IS_GL_VERTEX;
1017     num_gl_vert++;
1018   }
1019 
1020   // compute wall_tip info
1021 
1022   VertexAddWallTip(vert, -seg->pdx, -seg->pdy, seg->sector,
1023       seg->partner ? seg->partner->sector : NULL);
1024 
1025   VertexAddWallTip(vert, seg->pdx, seg->pdy,
1026       seg->partner ? seg->partner->sector : NULL, seg->sector);
1027 
1028   // create a duplex vertex if needed
1029 
1030   if (lev_doing_normal && cur_info->spec_version != 1)
1031   {
1032     vert->normal_dup = NewVertex();
1033 
1034     vert->normal_dup->x = x;
1035     vert->normal_dup->y = y;
1036     vert->normal_dup->ref_count = vert->ref_count;
1037 
1038     vert->normal_dup->index = num_normal_vert;
1039     num_normal_vert++;
1040   }
1041 
1042   return vert;
1043 }
1044 
1045 //
1046 // NewVertexDegenerate
1047 //
NewVertexDegenerate(vertex_t * start,vertex_t * end)1048 vertex_t *NewVertexDegenerate(vertex_t *start, vertex_t *end)
1049 {
1050   float_g dx = end->x - start->x;
1051   float_g dy = end->y - start->y;
1052 
1053   float_g dlen = UtilComputeDist(dx, dy);
1054 
1055   vertex_t *vert = NewVertex();
1056 
1057   vert->ref_count = start->ref_count;
1058 
1059   if (lev_doing_normal)
1060   {
1061     vert->index = num_normal_vert;
1062     num_normal_vert++;
1063   }
1064   else
1065   {
1066     vert->index = num_gl_vert | IS_GL_VERTEX;
1067     num_gl_vert++;
1068   }
1069 
1070   // compute new coordinates
1071 
1072   vert->x = start->x;
1073   vert->y = start->x;
1074 
1075   if (dlen == 0)
1076     InternalError("NewVertexDegenerate: bad delta !");
1077 
1078   dx /= dlen;
1079   dy /= dlen;
1080 
1081   while (I_ROUND(vert->x) == I_ROUND(start->x) &&
1082          I_ROUND(vert->y) == I_ROUND(start->y))
1083   {
1084     vert->x += dx;
1085     vert->y += dy;
1086   }
1087 
1088   return vert;
1089 }
1090 
1091 //
1092 // VertexCheckOpen
1093 //
VertexCheckOpen(vertex_t * vert,float_g dx,float_g dy)1094 sector_t * VertexCheckOpen(vertex_t *vert, float_g dx, float_g dy)
1095 {
1096   wall_tip_t *tip;
1097 
1098   angle_g angle = UtilComputeAngle(dx, dy);
1099 
1100   // first check whether there's a wall_tip that lies in the exact
1101   // direction of the given direction (which is relative to the
1102   // vertex).
1103 
1104   for (tip=vert->tip_set; tip; tip=tip->next)
1105   {
1106     if (fabs(tip->angle - angle) < ANG_EPSILON ||
1107         fabs(tip->angle - angle) > (360.0 - ANG_EPSILON))
1108     {
1109       // yes, found one
1110       return NULL;
1111     }
1112   }
1113 
1114   // OK, now just find the first wall_tip whose angle is greater than
1115   // the angle we're interested in.  Therefore we'll be on the RIGHT
1116   // side of that wall_tip.
1117 
1118   for (tip=vert->tip_set; tip; tip=tip->next)
1119   {
1120     if (angle + ANG_EPSILON < tip->angle)
1121     {
1122       // found it
1123       return tip->right;
1124     }
1125 
1126     if (! tip->next)
1127     {
1128       // no more tips, thus we must be on the LEFT side of the tip
1129       // with the largest angle.
1130 
1131       return tip->left;
1132     }
1133   }
1134 
1135   InternalError("Vertex %d has no tips !", vert->index);
1136   return FALSE;
1137 }
1138 
1139