add an option -mergeportals to speed up vis, while not making the vis data MUCH worse
[divverent/netradiant.git] / tools / quake3 / q3map2 / vis.c
1 /* -------------------------------------------------------------------------------
2
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6 This file is part of GtkRadiant.
7
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22 ----------------------------------------------------------------------------------
23
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27 ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define VIS_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41
42 void PlaneFromWinding (fixedWinding_t *w, visPlane_t *plane)
43 {
44         vec3_t          v1, v2;
45
46 // calc plane
47         VectorSubtract (w->points[2], w->points[1], v1);
48         VectorSubtract (w->points[0], w->points[1], v2);
49         CrossProduct (v2, v1, plane->normal);
50         VectorNormalize (plane->normal, plane->normal);
51         plane->dist = DotProduct (w->points[0], plane->normal);
52 }
53
54
55 /*
56 NewFixedWinding()
57 returns a new fixed winding
58 ydnar: altered this a bit to reconcile multiply-defined winding_t
59 */
60
61 fixedWinding_t *NewFixedWinding( int points )
62 {
63         fixedWinding_t  *w;
64         int                     size;
65         
66         if (points > MAX_POINTS_ON_WINDING)
67                 Error ("NewWinding: %i points", points);
68         
69         size = (int)((size_t)((fixedWinding_t *)0)->points[points]);
70         w = safe_malloc (size);
71         memset (w, 0, size);
72         
73         return w;
74 }
75
76
77
78 void prl(leaf_t *l)
79 {
80         int                     i;
81         vportal_t       *p;
82         visPlane_t      pl;
83         
84         for (i=0 ; i<l->numportals ; i++)
85         {
86                 p = l->portals[i];
87                 pl = p->plane;
88                 Sys_Printf ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
89         }
90 }
91
92
93 //=============================================================================
94
95 /*
96 =============
97 SortPortals
98
99 Sorts the portals from the least complex, so the later ones can reuse
100 the earlier information.
101 =============
102 */
103 int PComp (const void *a, const void *b)
104 {
105         if ( (*(vportal_t **)a)->nummightsee == (*(vportal_t **)b)->nummightsee)
106                 return 0;
107         if ( (*(vportal_t **)a)->nummightsee < (*(vportal_t **)b)->nummightsee)
108                 return -1;
109         return 1;
110 }
111 void SortPortals (void)
112 {
113         int             i;
114         
115         for (i=0 ; i<numportals*2 ; i++)
116                 sorted_portals[i] = &portals[i];
117
118         if (nosort)
119                 return;
120         qsort (sorted_portals, numportals*2, sizeof(sorted_portals[0]), PComp);
121 }
122
123
124 /*
125 ==============
126 LeafVectorFromPortalVector
127 ==============
128 */
129 int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
130 {
131         int                     i, j, leafnum;
132         vportal_t       *p;
133         int                     c_leafs;
134
135
136         for (i=0 ; i<numportals*2 ; i++)
137         {
138                 if (portalbits[i>>3] & (1<<(i&7)) )
139                 {
140                         p = portals+i;
141                         leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
142                 }
143         }
144
145         for (j = 0; j < portalclusters; j++)
146         {
147                 leafnum = j;
148                 while (leafs[leafnum].merged >= 0)
149                         leafnum = leafs[leafnum].merged;
150                 //if the merged leaf is visible then the original leaf is visible
151                 if (leafbits[leafnum>>3] & (1<<(leafnum&7)))
152                 {
153                         leafbits[j>>3] |= (1<<(j&7));
154                 }
155         }
156
157         c_leafs = CountBits (leafbits, portalclusters);
158
159         return c_leafs;
160 }
161
162
163 /*
164 ===============
165 ClusterMerge
166
167 Merges the portal visibility for a leaf
168 ===============
169 */
170 void ClusterMerge (int leafnum)
171 {
172         leaf_t          *leaf;
173         byte            portalvector[MAX_PORTALS/8];
174         byte            uncompressed[MAX_MAP_LEAFS/8];
175         int                     i, j;
176         int                     numvis, mergedleafnum;
177         vportal_t       *p;
178         int                     pnum;
179
180         // OR together all the portalvis bits
181
182         mergedleafnum = leafnum;
183         while(leafs[mergedleafnum].merged >= 0)
184                 mergedleafnum = leafs[mergedleafnum].merged;
185
186         memset (portalvector, 0, portalbytes);
187         leaf = &leafs[mergedleafnum];
188         for (i = 0; i < leaf->numportals; i++)
189         {
190                 p = leaf->portals[i];
191                 if (p->removed)
192                         continue;
193
194                 if (p->status != stat_done)
195                         Error ("portal not done");
196                 for (j=0 ; j<portallongs ; j++)
197                         ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
198                 pnum = p - portals;
199                 portalvector[pnum>>3] |= 1<<(pnum&7);
200         }
201
202         memset (uncompressed, 0, leafbytes);
203
204         uncompressed[mergedleafnum>>3] |= (1<<(mergedleafnum&7));
205         // convert portal bits to leaf bits
206         numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
207
208 //      if (uncompressed[leafnum>>3] & (1<<(leafnum&7)))
209 //              Sys_Printf ("WARNING: Leaf portals saw into leaf\n");
210                 
211 //      uncompressed[leafnum>>3] |= (1<<(leafnum&7));
212
213         numvis++;               // count the leaf itself
214
215         totalvis += numvis;
216
217         Sys_FPrintf (SYS_VRB,"cluster %4i : %4i visible\n", leafnum, numvis);
218
219         memcpy (bspVisBytes + VIS_HEADER_SIZE + leafnum*leafbytes, uncompressed, leafbytes);
220 }
221
222 /*
223 ==================
224 CalcPortalVis
225 ==================
226 */
227 void CalcPortalVis (void)
228 {
229 #ifdef MREDEBUG
230         Sys_Printf("%6d portals out of %d", 0, numportals*2);
231         //get rid of the counter
232         RunThreadsOnIndividual (numportals*2, qfalse, PortalFlow);
233 #else
234         RunThreadsOnIndividual (numportals*2, qtrue, PortalFlow);
235 #endif
236
237 }
238
239 /*
240 ==================
241 CalcPassageVis
242 ==================
243 */
244 void CalcPassageVis(void)
245 {
246         PassageMemory();
247
248 #ifdef MREDEBUG
249         _printf("%6d portals out of %d", 0, numportals*2);
250         RunThreadsOnIndividual (numportals*2, qfalse, CreatePassages);
251         _printf("\n");
252         _printf("%6d portals out of %d", 0, numportals*2);
253         RunThreadsOnIndividual (numportals*2, qfalse, PassageFlow);
254         _printf("\n");
255 #else
256         Sys_Printf( "\n--- CreatePassages (%d) ---\n", numportals * 2 );
257         RunThreadsOnIndividual( numportals*2, qtrue, CreatePassages );
258         
259         Sys_Printf( "\n--- PassageFlow (%d) ---\n", numportals * 2 );
260         RunThreadsOnIndividual( numportals * 2, qtrue, PassageFlow );
261 #endif
262 }
263
264 /*
265 ==================
266 CalcPassagePortalVis
267 ==================
268 */
269 void CalcPassagePortalVis(void)
270 {
271         PassageMemory();
272
273 #ifdef MREDEBUG
274         Sys_Printf("%6d portals out of %d", 0, numportals*2);
275         RunThreadsOnIndividual (numportals*2, qfalse, CreatePassages);
276         Sys_Printf("\n");
277         Sys_Printf("%6d portals out of %d", 0, numportals*2);
278         RunThreadsOnIndividual (numportals*2, qfalse, PassagePortalFlow);
279         Sys_Printf("\n");
280 #else
281         Sys_Printf( "\n--- CreatePassages (%d) ---\n", numportals * 2 );
282         RunThreadsOnIndividual( numportals * 2, qtrue, CreatePassages);
283         
284         Sys_Printf( "\n--- PassagePortalFlow (%d) ---\n", numportals * 2 );
285         RunThreadsOnIndividual( numportals * 2, qtrue, PassagePortalFlow );
286 #endif
287 }
288
289 /*
290 ==================
291 CalcFastVis
292 ==================
293 */
294 void CalcFastVis(void)
295 {
296         int             i;
297
298         // fastvis just uses mightsee for a very loose bound
299         for (i=0 ; i<numportals*2 ; i++)
300         {
301                 portals[i].portalvis = portals[i].portalflood;
302                 portals[i].status = stat_done;
303         }
304 }
305
306 /*
307 ==================
308 CalcVis
309 ==================
310 */
311 void CalcVis (void)
312 {
313         int                     i;
314         const char      *value;
315         
316         
317         /* ydnar: rr2do2's farplane code */
318         farPlaneDist = 0.0f;
319         value = ValueForKey( &entities[ 0 ], "_farplanedist" );         /* proper '_' prefixed key */
320         if( value[ 0 ] == '\0' )
321                 value = ValueForKey( &entities[ 0 ], "fogclip" );               /* wolf compatibility */
322         if( value[ 0 ] == '\0' )
323                 value = ValueForKey( &entities[ 0 ], "distancecull" );  /* sof2 compatibility */
324         if( value[ 0 ] != '\0' )
325         {
326                 farPlaneDist = atof( value );
327                 if( farPlaneDist > 0.0f )
328                         Sys_Printf( "farplane distance = %.1f\n", farPlaneDist );
329                 else
330                         farPlaneDist = 0.0f;
331         }
332         
333         
334         
335         Sys_Printf( "\n--- BasePortalVis (%d) ---\n", numportals * 2 );
336         RunThreadsOnIndividual( numportals * 2, qtrue, BasePortalVis );
337         
338 //      RunThreadsOnIndividual (numportals*2, qtrue, BetterPortalVis);
339
340         SortPortals ();
341
342   if (fastvis) {
343     CalcFastVis();
344   }
345   else if ( noPassageVis ) {
346     CalcPortalVis();
347   }
348   else if ( passageVisOnly ) {
349     CalcPassageVis();
350   }
351   else {
352     CalcPassagePortalVis();
353   }
354         //
355         // assemble the leaf vis lists by oring and compressing the portal lists
356         //
357         Sys_Printf("creating leaf vis...\n");
358         for (i=0 ; i<portalclusters ; i++)
359                 ClusterMerge (i);
360
361   Sys_Printf( "Total visible clusters: %i\n", totalvis );
362   Sys_Printf( "Average clusters visible: %i\n", totalvis / portalclusters );
363 }
364
365 /*
366 ==================
367 SetPortalSphere
368 ==================
369 */
370 void SetPortalSphere (vportal_t *p)
371 {
372         int             i;
373         vec3_t  total, dist;
374         fixedWinding_t  *w;
375         float   r, bestr;
376
377         w = p->winding;
378         VectorCopy (vec3_origin, total);
379         for (i=0 ; i<w->numpoints ; i++)
380         {
381                 VectorAdd (total, w->points[i], total);
382         }
383         
384         for (i=0 ; i<3 ; i++)
385                 total[i] /= w->numpoints;
386
387         bestr = 0;              
388         for (i=0 ; i<w->numpoints ; i++)
389         {
390                 VectorSubtract (w->points[i], total, dist);
391                 r = VectorLength (dist);
392                 if (r > bestr)
393                         bestr = r;
394         }
395         VectorCopy (total, p->origin);
396         p->radius = bestr;
397 }
398
399 /*
400 =============
401 Winding_PlanesConcave
402 =============
403 */
404 #define WCONVEX_EPSILON         0.2
405
406 int Winding_PlanesConcave(fixedWinding_t *w1, fixedWinding_t *w2,
407                                                          vec3_t normal1, vec3_t normal2,
408                                                          float dist1, float dist2)
409 {
410         int i;
411
412         if (!w1 || !w2) return qfalse;
413
414         // check if one of the points of winding 1 is at the front of the plane of winding 2
415         for (i = 0; i < w1->numpoints; i++)
416         {
417                 if (DotProduct(normal2, w1->points[i]) - dist2 > WCONVEX_EPSILON) return qtrue;
418         }
419         // check if one of the points of winding 2 is at the front of the plane of winding 1
420         for (i = 0; i < w2->numpoints; i++)
421         {
422                 if (DotProduct(normal1, w2->points[i]) - dist1 > WCONVEX_EPSILON) return qtrue;
423         }
424
425         return qfalse;
426 }
427
428 /*
429 ============
430 TryMergeLeaves
431 ============
432 */
433 int TryMergeLeaves(int l1num, int l2num)
434 {
435         int i, j, k, n, numportals;
436         visPlane_t plane1, plane2;
437         leaf_t *l1, *l2;
438         vportal_t *p1, *p2;
439         vportal_t *portals[MAX_PORTALS_ON_LEAF];
440
441         for (k = 0; k < 2; k++)
442         {
443                 if (k) l1 = &leafs[l1num];
444                 else l1 = &faceleafs[l1num];
445                 for (i = 0; i < l1->numportals; i++)
446                 {
447                         p1 = l1->portals[i];
448                         if (p1->leaf == l2num) continue;
449                         for (n = 0; n < 2; n++)
450                         {
451                                 if (n) l2 = &leafs[l2num];
452                                 else l2 = &faceleafs[l2num];
453                                 for (j = 0; j < l2->numportals; j++)
454                                 {
455                                         p2 = l2->portals[j];
456                                         if (p2->leaf == l1num) continue;
457                                         //
458                                         plane1 = p1->plane;
459                                         plane2 = p2->plane;
460                                         if (Winding_PlanesConcave(p1->winding, p2->winding, plane1.normal, plane2.normal, plane1.dist, plane2.dist))
461                                                 return qfalse;
462                                 }
463                         }
464                 }
465         }
466         for (k = 0; k < 2; k++)
467         {
468                 if (k)
469                 {
470                         l1 = &leafs[l1num];
471                         l2 = &leafs[l2num];
472                 }
473                 else
474                 {
475                         l1 = &faceleafs[l1num];
476                         l2 = &faceleafs[l2num];
477                 }
478                 numportals = 0;
479                 //the leaves can be merged now
480                 for (i = 0; i < l1->numportals; i++)
481                 {
482                         p1 = l1->portals[i];
483                         if (p1->leaf == l2num)
484                         {
485                                 p1->removed = qtrue;
486                                 continue;
487                         }
488                         portals[numportals++] = p1;
489                 }
490                 for (j = 0; j < l2->numportals; j++)
491                 {
492                         p2 = l2->portals[j];
493                         if (p2->leaf == l1num)
494                         {
495                                 p2->removed = qtrue;
496                                 continue;
497                         }
498                         portals[numportals++] = p2;
499                 }
500                 for (i = 0; i < numportals; i++)
501                 {
502                         l2->portals[i] = portals[i];
503                 }
504                 l2->numportals = numportals;
505                 l1->merged = l2num;
506         }
507         return qtrue;
508 }
509
510 /*
511 ============
512 UpdatePortals
513 ============
514 */
515 void UpdatePortals(void)
516 {
517         int i;
518         vportal_t *p;
519
520         for (i = 0; i < numportals * 2; i++)
521         {
522                 p = &portals[i];
523                 if (p->removed)
524                         continue;
525                 while(leafs[p->leaf].merged >= 0)
526                         p->leaf = leafs[p->leaf].merged;
527         }
528 }
529
530 /*
531 ============
532 MergeLeaves
533
534 try to merge leaves but don't merge through hint splitters
535 ============
536 */
537 void MergeLeaves(void)
538 {
539         int i, j, nummerges, totalnummerges;
540         leaf_t *leaf;
541         vportal_t *p;
542
543         totalnummerges = 0;
544         do
545         {
546                 nummerges = 0;
547                 for (i = 0; i < portalclusters; i++)
548                 {
549                         leaf = &leafs[i];
550                         //if this leaf is merged already
551
552                         /* ydnar: vmods: merge all non-hint portals */
553                         if( leaf->merged >= 0 && hint == qfalse )
554                                 continue;
555
556
557                         for (j = 0; j < leaf->numportals; j++)
558                         {
559                                 p = leaf->portals[j];
560                                 //
561                                 if (p->removed)
562                                         continue;
563                                 //never merge through hint portals
564                                 if (p->hint)
565                                         continue;
566                                 if (TryMergeLeaves(i, p->leaf))
567                                 {
568                                         UpdatePortals();
569                                         nummerges++;
570                                         break;
571                                 }
572                         }
573                 }
574                 totalnummerges += nummerges;
575         } while (nummerges);
576         Sys_Printf("%6d leaves merged\n", totalnummerges);
577 }
578
579 /*
580 ============
581 TryMergeWinding
582 ============
583 */
584 #define CONTINUOUS_EPSILON      0.005
585
586 fixedWinding_t *TryMergeWinding (fixedWinding_t *f1, fixedWinding_t *f2, vec3_t planenormal)
587 {
588         vec_t           *p1, *p2, *p3, *p4, *back;
589         fixedWinding_t  *newf;
590         int                     i, j, k, l;
591         vec3_t          normal, delta;
592         vec_t           dot;
593         qboolean        keep1, keep2;
594         
595
596         //
597         // find a common edge
598         //      
599         p1 = p2 = NULL; // stop compiler warning
600         j = 0;                  // 
601         
602         for (i = 0; i < f1->numpoints; i++)
603         {
604                 p1 = f1->points[i];
605                 p2 = f1->points[(i+1) % f1->numpoints];
606                 for (j = 0; j < f2->numpoints; j++)
607                 {
608                         p3 = f2->points[j];
609                         p4 = f2->points[(j+1) % f2->numpoints];
610                         for (k = 0; k < 3; k++)
611                         {
612                                 if (fabs(p1[k] - p4[k]) > 0.1)//EQUAL_EPSILON) //ME
613                                         break;
614                                 if (fabs(p2[k] - p3[k]) > 0.1)//EQUAL_EPSILON) //ME
615                                         break;
616                         } //end for
617                         if (k==3)
618                                 break;
619                 } //end for
620                 if (j < f2->numpoints)
621                         break;
622         } //end for
623         
624         if (i == f1->numpoints)
625                 return NULL;                    // no matching edges
626
627         //
628         // check slope of connected lines
629         // if the slopes are colinear, the point can be removed
630         //
631         back = f1->points[(i+f1->numpoints-1)%f1->numpoints];
632         VectorSubtract (p1, back, delta);
633         CrossProduct (planenormal, delta, normal);
634         VectorNormalize (normal, normal);
635         
636         back = f2->points[(j+2)%f2->numpoints];
637         VectorSubtract (back, p1, delta);
638         dot = DotProduct (delta, normal);
639         if (dot > CONTINUOUS_EPSILON)
640                 return NULL;                    // not a convex polygon
641         keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
642         
643         back = f1->points[(i+2)%f1->numpoints];
644         VectorSubtract (back, p2, delta);
645         CrossProduct (planenormal, delta, normal);
646         VectorNormalize (normal, normal);
647
648         back = f2->points[(j+f2->numpoints-1)%f2->numpoints];
649         VectorSubtract (back, p2, delta);
650         dot = DotProduct (delta, normal);
651         if (dot > CONTINUOUS_EPSILON)
652                 return NULL;                    // not a convex polygon
653         keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
654
655         //
656         // build the new polygon
657         //
658         newf = NewFixedWinding (f1->numpoints + f2->numpoints);
659         
660         // copy first polygon
661         for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
662         {
663                 if (k==(i+1)%f1->numpoints && !keep2)
664                         continue;
665                 
666                 VectorCopy (f1->points[k], newf->points[newf->numpoints]);
667                 newf->numpoints++;
668         }
669         
670         // copy second polygon
671         for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
672         {
673                 if (l==(j+1)%f2->numpoints && !keep1)
674                         continue;
675                 VectorCopy (f2->points[l], newf->points[newf->numpoints]);
676                 newf->numpoints++;
677         }
678
679         return newf;
680 }
681
682 /*
683 ============
684 MergeLeafPortals
685 ============
686 */
687 void MergeLeafPortals(void)
688 {
689         int i, j, k, nummerges, hintsmerged;
690         leaf_t *leaf;
691         vportal_t *p1, *p2;
692         fixedWinding_t *w;
693
694         nummerges = 0;
695         hintsmerged = 0;
696         for (i = 0; i < portalclusters; i++)
697         {
698                 leaf = &leafs[i];
699                 if (leaf->merged >= 0) continue;
700                 for (j = 0; j < leaf->numportals; j++)
701                 {
702                         p1 = leaf->portals[j];
703                         if (p1->removed)
704                                 continue;
705                         for (k = j+1; k < leaf->numportals; k++)
706                         {
707                                 p2 = leaf->portals[k];
708                                 if (p2->removed)
709                                         continue;
710                                 if (p1->leaf == p2->leaf)
711                                 {
712                                         w = TryMergeWinding(p1->winding, p2->winding, p1->plane.normal);
713                                         if (w)
714                                         {
715                                                 free( p1->winding );    //% FreeWinding(p1->winding);
716                                                 p1->winding = w;
717                                                 if (p1->hint && p2->hint)
718                                                         hintsmerged++;
719                                                 p1->hint |= p2->hint;
720                                                 SetPortalSphere(p1);
721                                                 p2->removed = qtrue;
722                                                 nummerges++;
723                                                 i--;
724                                                 break;
725                                         }
726                                 }
727                         }
728                         if (k < leaf->numportals)
729                                 break;
730                 }
731         }
732         Sys_Printf("%6d portals merged\n", nummerges);
733         Sys_Printf("%6d hint portals merged\n", hintsmerged);
734 }
735
736
737 /*
738 ============
739 WritePortals
740 ============
741 */
742 int CountActivePortals(void)
743 {
744         int num, hints, j;
745         vportal_t *p;
746
747         num = 0;
748         hints = 0;
749         for (j = 0; j < numportals * 2; j++)
750         {
751                 p = portals + j;
752                 if (p->removed)
753                         continue;
754                 if (p->hint)
755                         hints++;
756                 num++;
757         }
758         Sys_Printf("%6d active portals\n", num);
759         Sys_Printf("%6d hint portals\n", hints);
760         return num;
761 }
762
763 /*
764 ============
765 WritePortals
766 ============
767 */
768 void WriteFloat (FILE *f, vec_t v);
769
770 void WritePortals(char *filename)
771 {
772         int i, j, num;
773         FILE *pf;
774         vportal_t *p;
775         fixedWinding_t *w;
776
777         // write the file
778         pf = fopen (filename, "w");
779         if (!pf)
780                 Error ("Error opening %s", filename);
781
782         num = 0;
783         for (j = 0; j < numportals * 2; j++)
784         {
785                 p = portals + j;
786                 if (p->removed)
787                         continue;
788 //              if (!p->hint)
789 //                      continue;
790                 num++;
791         }
792
793         fprintf (pf, "%s\n", PORTALFILE);
794         fprintf (pf, "%i\n", 0);
795         fprintf (pf, "%i\n", num);// + numfaces);
796         fprintf (pf, "%i\n", 0);
797
798         for (j = 0; j < numportals * 2; j++)
799         {
800                 p = portals + j;
801                 if (p->removed)
802                         continue;
803 //              if (!p->hint)
804 //                      continue;
805                 w = p->winding;
806                 fprintf (pf,"%i %i %i ",w->numpoints, 0, 0);
807                 fprintf (pf, "%d ", p->hint);
808                 for (i=0 ; i<w->numpoints ; i++)
809                 {
810                         fprintf (pf,"(");
811                         WriteFloat (pf, w->points[i][0]);
812                         WriteFloat (pf, w->points[i][1]);
813                         WriteFloat (pf, w->points[i][2]);
814                         fprintf (pf,") ");
815                 }
816                 fprintf (pf,"\n");
817         }
818
819         /*
820         for (j = 0; j < numfaces; j++)
821         {
822                 p = faces + j;
823                 w = p->winding;
824                 fprintf (pf,"%i %i %i ",w->numpoints, 0, 0);
825                 fprintf (pf, "0 ");
826                 for (i=0 ; i<w->numpoints ; i++)
827                 {
828                         fprintf (pf,"(");
829                         WriteFloat (pf, w->points[i][0]);
830                         WriteFloat (pf, w->points[i][1]);
831                         WriteFloat (pf, w->points[i][2]);
832                         fprintf (pf,") ");
833                 }
834                 fprintf (pf,"\n");
835         }*/
836
837         fclose (pf);
838 }
839
840 /*
841 ============
842 LoadPortals
843 ============
844 */
845 void LoadPortals (char *name)
846 {
847         int                     i, j, hint;
848         vportal_t       *p;
849         leaf_t          *l;
850         char            magic[80];
851         FILE            *f;
852         int                     numpoints;
853         fixedWinding_t  *w;
854         int                     leafnums[2];
855         visPlane_t      plane;
856         
857         if (!strcmp(name,"-"))
858                 f = stdin;
859         else
860         {
861                 f = fopen(name, "r");
862                 if (!f)
863                         Error ("LoadPortals: couldn't read %s\n",name);
864         }
865
866         if (fscanf (f,"%79s\n%i\n%i\n%i\n",magic, &portalclusters, &numportals, &numfaces) != 4)
867                 Error ("LoadPortals: failed to read header");
868         if (strcmp(magic,PORTALFILE))
869                 Error ("LoadPortals: not a portal file");
870
871         Sys_Printf ("%6i portalclusters\n", portalclusters);
872         Sys_Printf ("%6i numportals\n", numportals);
873         Sys_Printf ("%6i numfaces\n", numfaces);
874         
875         // these counts should take advantage of 64 bit systems automatically
876         leafbytes = ((portalclusters+63)&~63)>>3;
877         leaflongs = leafbytes/sizeof(long);
878         
879         portalbytes = ((numportals*2+63)&~63)>>3;
880         portallongs = portalbytes/sizeof(long);
881
882         // each file portal is split into two memory portals
883         portals = safe_malloc(2*numportals*sizeof(vportal_t));
884         memset (portals, 0, 2*numportals*sizeof(vportal_t));
885         
886         leafs = safe_malloc(portalclusters*sizeof(leaf_t));
887         memset (leafs, 0, portalclusters*sizeof(leaf_t));
888
889         for (i = 0; i < portalclusters; i++)
890                 leafs[i].merged = -1;
891
892         numBSPVisBytes = VIS_HEADER_SIZE + portalclusters*leafbytes;
893
894         if (numBSPVisBytes > MAX_MAP_VISIBILITY)
895           Error("MAX_MAP_VISIBILITY exceeded");
896
897         ((int *)bspVisBytes)[0] = portalclusters;
898         ((int *)bspVisBytes)[1] = leafbytes;
899                 
900         for (i=0, p=portals ; i<numportals ; i++)
901         {
902                 if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)
903                         Error ("LoadPortals: reading portal %i", i);
904                 if (numpoints > MAX_POINTS_ON_WINDING)
905                         Error ("LoadPortals: portal %i has too many points", i);
906                 if ( (unsigned)leafnums[0] > portalclusters
907                 || (unsigned)leafnums[1] > portalclusters)
908                         Error ("LoadPortals: reading portal %i", i);
909                 if (fscanf (f, "%i ", &hint) != 1)
910                         Error ("LoadPortals: reading hint state");
911                 
912                 w = p->winding = NewFixedWinding (numpoints);
913                 w->numpoints = numpoints;
914                 
915                 for (j=0 ; j<numpoints ; j++)
916                 {
917                         double  v[3];
918                         int             k;
919
920                         // scanf into double, then assign to vec_t
921                         // so we don't care what size vec_t is
922                         if (fscanf (f, "(%lf %lf %lf ) "
923                         , &v[0], &v[1], &v[2]) != 3)
924                                 Error ("LoadPortals: reading portal %i", i);
925                         for (k=0 ; k<3 ; k++)
926                                 w->points[j][k] = v[k];
927                 }
928                 fscanf (f, "\n");
929                 
930                 // calc plane
931                 PlaneFromWinding (w, &plane);
932
933                 // create forward portal
934                 l = &leafs[leafnums[0]];
935                 if (l->numportals == MAX_PORTALS_ON_LEAF)
936                         Error ("Leaf with too many portals");
937                 l->portals[l->numportals] = p;
938                 l->numportals++;
939                 
940                 p->num = i+1;
941                 p->hint = hint;
942                 p->winding = w;
943                 VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
944                 p->plane.dist = -plane.dist;
945                 p->leaf = leafnums[1];
946                 SetPortalSphere (p);
947                 p++;
948                 
949                 // create backwards portal
950                 l = &leafs[leafnums[1]];
951                 if (l->numportals == MAX_PORTALS_ON_LEAF)
952                         Error ("Leaf with too many portals");
953                 l->portals[l->numportals] = p;
954                 l->numportals++;
955                 
956                 p->num = i+1;
957                 p->hint = hint;
958                 p->winding = NewFixedWinding(w->numpoints);
959                 p->winding->numpoints = w->numpoints;
960                 for (j=0 ; j<w->numpoints ; j++)
961                 {
962                         VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
963                 }
964
965                 p->plane = plane;
966                 p->leaf = leafnums[0];
967                 SetPortalSphere (p);
968                 p++;
969
970         }
971
972         faces = safe_malloc(2*numfaces*sizeof(vportal_t));
973         memset (faces, 0, 2*numfaces*sizeof(vportal_t));
974
975         faceleafs = safe_malloc(portalclusters*sizeof(leaf_t));
976         memset(faceleafs, 0, portalclusters*sizeof(leaf_t));
977
978         for (i = 0, p = faces; i < numfaces; i++)
979         {
980                 if (fscanf (f, "%i %i ", &numpoints, &leafnums[0]) != 2)
981                         Error ("LoadPortals: reading portal %i", i);
982
983                 w = p->winding = NewFixedWinding (numpoints);
984                 w->numpoints = numpoints;
985                 
986                 for (j=0 ; j<numpoints ; j++)
987                 {
988                         double  v[3];
989                         int             k;
990
991                         // scanf into double, then assign to vec_t
992                         // so we don't care what size vec_t is
993                         if (fscanf (f, "(%lf %lf %lf ) "
994                         , &v[0], &v[1], &v[2]) != 3)
995                                 Error ("LoadPortals: reading portal %i", i);
996                         for (k=0 ; k<3 ; k++)
997                                 w->points[j][k] = v[k];
998                 }
999                 fscanf (f, "\n");
1000                 
1001                 // calc plane
1002                 PlaneFromWinding (w, &plane);
1003
1004                 l = &faceleafs[leafnums[0]];
1005                 l->merged = -1;
1006                 if (l->numportals == MAX_PORTALS_ON_LEAF)
1007                         Error ("Leaf with too many faces");
1008                 l->portals[l->numportals] = p;
1009                 l->numportals++;
1010                 
1011                 p->num = i+1;
1012                 p->winding = w;
1013                 // normal pointing out of the leaf
1014                 VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
1015                 p->plane.dist = -plane.dist;
1016                 p->leaf = -1;
1017                 SetPortalSphere (p);
1018                 p++;
1019         }
1020         
1021         fclose (f);
1022 }
1023
1024
1025
1026 /*
1027 ===========
1028 VisMain
1029 ===========
1030 */
1031 int VisMain (int argc, char **argv)
1032 {
1033         char            portalfile[1024];
1034         int                     i;
1035         
1036         
1037         /* note it */
1038         Sys_Printf( "--- Vis ---\n" );
1039         
1040         /* process arguments */
1041         for (i=1 ; i < (argc - 1) ; i++)
1042         {
1043                 if (!strcmp(argv[i], "-fast")) {
1044                         Sys_Printf ("fastvis = true\n");
1045                         fastvis = qtrue;
1046                 } else if (!strcmp(argv[i], "-merge")) {
1047                         Sys_Printf ("merge = true\n");
1048                         mergevis = qtrue;
1049                 } else if (!strcmp(argv[i], "-mergeportals")) {
1050                         Sys_Printf ("mergeportals = true\n");
1051                         mergevisportals = qtrue;
1052                 } else if (!strcmp(argv[i], "-nopassage")) {
1053                         Sys_Printf ("nopassage = true\n");
1054                         noPassageVis = qtrue;
1055                 } else if (!strcmp(argv[i], "-passageOnly")) {
1056                         Sys_Printf ("passageOnly = true\n");
1057                         passageVisOnly = qtrue;
1058                 } else if (!strcmp (argv[i],"-nosort")) {
1059                         Sys_Printf ("nosort = true\n");
1060                         nosort = qtrue;
1061                 } else if (!strcmp (argv[i],"-saveprt")) {
1062                         Sys_Printf ("saveprt = true\n");
1063                         saveprt = qtrue;
1064                 } else if (!strcmp (argv[i],"-tmpin")) {
1065                         strcpy (inbase, "/tmp");
1066                 } else if (!strcmp (argv[i],"-tmpout")) {
1067                         strcpy (outbase, "/tmp");
1068                 }
1069                 
1070         
1071                 /* ydnar: -hint to merge all but hint portals */
1072                 else if( !strcmp( argv[ i ], "-hint" ) )
1073                 {
1074                         Sys_Printf( "hint = true\n" );
1075                         hint = qtrue;
1076                         mergevis = qtrue;
1077                 }
1078                 
1079                 else
1080                 {
1081                         Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );
1082                 }
1083         }
1084
1085         if( i != argc - 1 )
1086                 Error( "usage: vis [-threads #] [-level 0-4] [-fast] [-v] bspfile" );
1087         
1088
1089         /* load the bsp */
1090         sprintf( source, "%s%s", inbase, ExpandArg( argv[ i ] ) );
1091         StripExtension( source );
1092         strcat( source, ".bsp" );
1093         Sys_Printf( "Loading %s\n", source );
1094         LoadBSPFile( source );
1095         
1096         /* load the portal file */
1097         sprintf( portalfile, "%s%s", inbase, ExpandArg( argv[ i ] ) );
1098         StripExtension( portalfile );
1099         strcat( portalfile, ".prt" );
1100         Sys_Printf( "Loading %s\n", portalfile );
1101         LoadPortals( portalfile );
1102         
1103         /* ydnar: exit if no portals, hence no vis */
1104         if( numportals == 0 )
1105         {
1106                 Sys_Printf( "No portals means no vis, exiting.\n" );
1107                 return 0;
1108         }
1109         
1110         /* ydnar: for getting far plane */
1111         ParseEntities();
1112         
1113         /* inject command line parameters */
1114         InjectCommandLine(argv, 0, argc - 1);
1115         UnparseEntities();
1116         
1117         if( mergevis )
1118                 MergeLeaves();
1119
1120         if( mergevis || mergevisportals )
1121                 MergeLeafPortals();
1122         
1123         CountActivePortals();
1124         /* WritePortals( "maps/hints.prs" );*/
1125         
1126         Sys_Printf( "visdatasize:%i\n", numBSPVisBytes );
1127         
1128         CalcVis();
1129         
1130         /* delete the prt file */
1131         if( !saveprt )
1132                 remove( portalfile );
1133
1134         /* write the bsp file */
1135         Sys_Printf( "Writing %s\n", source );
1136         WriteBSPFile( source );
1137
1138         return 0;
1139 }