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