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 if (line->self_ref && cur_info->skip_self_ref)
957 continue;
958
959 float_g x1 = line->start->x;
960 float_g y1 = line->start->y;
961 float_g x2 = line->end->x;
962 float_g y2 = line->end->y;
963
964 sector_t *left = (line->left) ? line->left->sector : NULL;
965 sector_t *right = (line->right) ? line->right->sector : NULL;
966
967 VertexAddWallTip(line->start, x2-x1, y2-y1, left, right);
968 VertexAddWallTip(line->end, x1-x2, y1-y2, right, left);
969 }
970
971 # if DEBUG_WALLTIPS
972 for (i=0; i < num_vertices; i++)
973 {
974 vertex_t *vert = LookupVertex(i);
975 wall_tip_t *tip;
976
977 PrintDebug("WallTips for vertex %d:\n", i);
978
979 for (tip=vert->tip_set; tip; tip=tip->next)
980 {
981 PrintDebug(" Angle=%1.1f left=%d right=%d\n", tip->angle,
982 tip->left ? tip->left->index : -1,
983 tip->right ? tip->right->index : -1);
984 }
985 }
986 # endif
987 }
988
989 //
990 // NewVertexFromSplitSeg
991 //
NewVertexFromSplitSeg(seg_t * seg,float_g x,float_g y)992 vertex_t *NewVertexFromSplitSeg(seg_t *seg, float_g x, float_g y)
993 {
994 vertex_t *vert = NewVertex();
995
996 vert->x = x;
997 vert->y = y;
998
999 vert->ref_count = seg->partner ? 4 : 2;
1000
1001 if (lev_doing_normal && cur_info->spec_version == 1)
1002 {
1003 vert->index = num_normal_vert;
1004 num_normal_vert++;
1005 }
1006 else
1007 {
1008 vert->index = num_gl_vert | IS_GL_VERTEX;
1009 num_gl_vert++;
1010 }
1011
1012 // compute wall_tip info
1013
1014 VertexAddWallTip(vert, -seg->pdx, -seg->pdy, seg->sector,
1015 seg->partner ? seg->partner->sector : NULL);
1016
1017 VertexAddWallTip(vert, seg->pdx, seg->pdy,
1018 seg->partner ? seg->partner->sector : NULL, seg->sector);
1019
1020 // create a duplex vertex if needed
1021
1022 if (lev_doing_normal && cur_info->spec_version != 1)
1023 {
1024 vert->normal_dup = NewVertex();
1025
1026 vert->normal_dup->x = x;
1027 vert->normal_dup->y = y;
1028 vert->normal_dup->ref_count = vert->ref_count;
1029
1030 vert->normal_dup->index = num_normal_vert;
1031 num_normal_vert++;
1032 }
1033
1034 return vert;
1035 }
1036
1037 //
1038 // NewVertexDegenerate
1039 //
NewVertexDegenerate(vertex_t * start,vertex_t * end)1040 vertex_t *NewVertexDegenerate(vertex_t *start, vertex_t *end)
1041 {
1042 float_g dx = end->x - start->x;
1043 float_g dy = end->y - start->y;
1044
1045 float_g dlen = UtilComputeDist(dx, dy);
1046
1047 vertex_t *vert = NewVertex();
1048
1049 vert->ref_count = start->ref_count;
1050
1051 if (lev_doing_normal)
1052 {
1053 vert->index = num_normal_vert;
1054 num_normal_vert++;
1055 }
1056 else
1057 {
1058 vert->index = num_gl_vert | IS_GL_VERTEX;
1059 num_gl_vert++;
1060 }
1061
1062 // compute new coordinates
1063
1064 vert->x = start->x;
1065 vert->y = start->x;
1066
1067 if (dlen == 0)
1068 InternalError("NewVertexDegenerate: bad delta !");
1069
1070 dx /= dlen;
1071 dy /= dlen;
1072
1073 while (I_ROUND(vert->x) == I_ROUND(start->x) &&
1074 I_ROUND(vert->y) == I_ROUND(start->y))
1075 {
1076 vert->x += dx;
1077 vert->y += dy;
1078 }
1079
1080 return vert;
1081 }
1082
1083 //
1084 // VertexCheckOpen
1085 //
VertexCheckOpen(vertex_t * vert,float_g dx,float_g dy)1086 sector_t * VertexCheckOpen(vertex_t *vert, float_g dx, float_g dy)
1087 {
1088 wall_tip_t *tip;
1089
1090 angle_g angle = UtilComputeAngle(dx, dy);
1091
1092 // first check whether there's a wall_tip that lies in the exact
1093 // direction of the given direction (which is relative to the
1094 // vertex).
1095
1096 for (tip=vert->tip_set; tip; tip=tip->next)
1097 {
1098 if (fabs(tip->angle - angle) < ANG_EPSILON ||
1099 fabs(tip->angle - angle) > (360.0 - ANG_EPSILON))
1100 {
1101 // yes, found one
1102 return NULL;
1103 }
1104 }
1105
1106 // OK, now just find the first wall_tip whose angle is greater than
1107 // the angle we're interested in. Therefore we'll be on the RIGHT
1108 // side of that wall_tip.
1109
1110 for (tip=vert->tip_set; tip; tip=tip->next)
1111 {
1112 if (angle + ANG_EPSILON < tip->angle)
1113 {
1114 // found it
1115 return tip->right;
1116 }
1117
1118 if (! tip->next)
1119 {
1120 // no more tips, thus we must be on the LEFT side of the tip
1121 // with the largest angle.
1122
1123 return tip->left;
1124 }
1125 }
1126
1127 InternalError("Vertex %d has no tips !", vert->index);
1128 return FALSE;
1129 }
1130
1131