Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

vis.c (25165B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // vis.c
     23 
     24 #include "vis.h"
     25 #include "threads.h"
     26 #include "stdlib.h"
     27 #ifdef _WIN32
     28 #include "../libs/pakstuff.h"
     29 #endif
     30 
     31 
     32 #define	VIS_HEADER_SIZE	8
     33 
     34 extern	char		outbase[32];
     35 
     36 int			numportals;
     37 int			portalclusters;
     38 int			numfaces;
     39 
     40 char		inbase[32];
     41 
     42 vportal_t	*portals;
     43 leaf_t		*leafs;
     44 
     45 vportal_t	*faces;
     46 leaf_t		*faceleafs;
     47 
     48 int			c_portaltest, c_portalpass, c_portalcheck;
     49 
     50 int		leafbytes;				// (portalclusters+63)>>3
     51 int		leaflongs;
     52 
     53 int		portalbytes, portallongs;
     54 
     55 qboolean		fastvis;
     56 qboolean		noPassageVis;
     57 qboolean		passageVisOnly;
     58 qboolean		mergevis;
     59 qboolean		nosort;
     60 qboolean		saveprt;
     61 
     62 int			testlevel = 2;
     63 
     64 int		totalvis;
     65 
     66 vportal_t	*sorted_portals[MAX_MAP_PORTALS*2];
     67 
     68 void PassageMemory(void);
     69 
     70 
     71 //=============================================================================
     72 
     73 void PlaneFromWinding (winding_t *w, plane_t *plane)
     74 {
     75 	vec3_t		v1, v2;
     76 
     77 // calc plane
     78 	VectorSubtract (w->points[2], w->points[1], v1);
     79 	VectorSubtract (w->points[0], w->points[1], v2);
     80 	CrossProduct (v2, v1, plane->normal);
     81 	VectorNormalize (plane->normal, plane->normal);
     82 	plane->dist = DotProduct (w->points[0], plane->normal);
     83 }
     84 
     85 
     86 /*
     87 ==================
     88 NewWinding
     89 ==================
     90 */
     91 winding_t *NewWinding (int points)
     92 {
     93 	winding_t	*w;
     94 	int			size;
     95 	
     96 	if (points > MAX_POINTS_ON_WINDING)
     97 		Error ("NewWinding: %i points", points);
     98 	
     99 	size = (int)((winding_t *)0)->points[points];
    100 	w = malloc (size);
    101 	memset (w, 0, size);
    102 	
    103 	return w;
    104 }
    105 
    106 
    107 
    108 void prl(leaf_t *l)
    109 {
    110 	int			i;
    111 	vportal_t	*p;
    112 	plane_t		pl;
    113 	
    114 	for (i=0 ; i<l->numportals ; i++)
    115 	{
    116 		p = l->portals[i];
    117 		pl = p->plane;
    118 		_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]);
    119 	}
    120 }
    121 
    122 
    123 //=============================================================================
    124 
    125 /*
    126 =============
    127 SortPortals
    128 
    129 Sorts the portals from the least complex, so the later ones can reuse
    130 the earlier information.
    131 =============
    132 */
    133 int PComp (const void *a, const void *b)
    134 {
    135 	if ( (*(vportal_t **)a)->nummightsee == (*(vportal_t **)b)->nummightsee)
    136 		return 0;
    137 	if ( (*(vportal_t **)a)->nummightsee < (*(vportal_t **)b)->nummightsee)
    138 		return -1;
    139 	return 1;
    140 }
    141 void SortPortals (void)
    142 {
    143 	int		i;
    144 	
    145 	for (i=0 ; i<numportals*2 ; i++)
    146 		sorted_portals[i] = &portals[i];
    147 
    148 	if (nosort)
    149 		return;
    150 	qsort (sorted_portals, numportals*2, sizeof(sorted_portals[0]), PComp);
    151 }
    152 
    153 
    154 /*
    155 ==============
    156 LeafVectorFromPortalVector
    157 ==============
    158 */
    159 int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
    160 {
    161 	int			i, j, leafnum;
    162 	vportal_t	*p;
    163 	int			c_leafs;
    164 
    165 
    166 	for (i=0 ; i<numportals*2 ; i++)
    167 	{
    168 		if (portalbits[i>>3] & (1<<(i&7)) )
    169 		{
    170 			p = portals+i;
    171 			leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
    172 		}
    173 	}
    174 
    175 	for (j = 0; j < portalclusters; j++)
    176 	{
    177 		leafnum = j;
    178 		while (leafs[leafnum].merged >= 0)
    179 			leafnum = leafs[leafnum].merged;
    180 		//if the merged leaf is visible then the original leaf is visible
    181 		if (leafbits[leafnum>>3] & (1<<(leafnum&7)))
    182 		{
    183 			leafbits[j>>3] |= (1<<(j&7));
    184 		}
    185 	}
    186 
    187 	c_leafs = CountBits (leafbits, portalclusters);
    188 
    189 	return c_leafs;
    190 }
    191 
    192 
    193 /*
    194 ===============
    195 ClusterMerge
    196 
    197 Merges the portal visibility for a leaf
    198 ===============
    199 */
    200 void ClusterMerge (int leafnum)
    201 {
    202 	leaf_t		*leaf;
    203 	byte		portalvector[MAX_PORTALS/8];
    204 	byte		uncompressed[MAX_MAP_LEAFS/8];
    205 	int			i, j;
    206 	int			numvis, mergedleafnum;
    207 	vportal_t	*p;
    208 	int			pnum;
    209 
    210 	// OR together all the portalvis bits
    211 
    212 	mergedleafnum = leafnum;
    213 	while(leafs[mergedleafnum].merged >= 0)
    214 		mergedleafnum = leafs[mergedleafnum].merged;
    215 
    216 	memset (portalvector, 0, portalbytes);
    217 	leaf = &leafs[mergedleafnum];
    218 	for (i = 0; i < leaf->numportals; i++)
    219 	{
    220 		p = leaf->portals[i];
    221 		if (p->removed)
    222 			continue;
    223 
    224 		if (p->status != stat_done)
    225 			Error ("portal not done");
    226 		for (j=0 ; j<portallongs ; j++)
    227 			((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
    228 		pnum = p - portals;
    229 		portalvector[pnum>>3] |= 1<<(pnum&7);
    230 	}
    231 
    232 	memset (uncompressed, 0, leafbytes);
    233 
    234 	uncompressed[mergedleafnum>>3] |= (1<<(mergedleafnum&7));
    235 	// convert portal bits to leaf bits
    236 	numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
    237 
    238 //	if (uncompressed[leafnum>>3] & (1<<(leafnum&7)))
    239 //		_printf ("WARNING: Leaf portals saw into leaf\n");
    240 		
    241 //	uncompressed[leafnum>>3] |= (1<<(leafnum&7));
    242 
    243 	numvis++;		// count the leaf itself
    244 
    245 	totalvis += numvis;
    246 
    247 	qprintf ("cluster %4i : %4i visible\n", leafnum, numvis);
    248 
    249 	memcpy (visBytes + VIS_HEADER_SIZE + leafnum*leafbytes, uncompressed, leafbytes);
    250 }
    251 
    252 /*
    253 ==================
    254 CalcPortalVis
    255 ==================
    256 */
    257 void CalcPortalVis (void)
    258 {
    259 #ifdef MREDEBUG
    260 	_printf("%6d portals out of %d", 0, numportals*2);
    261 	//get rid of the counter
    262 	RunThreadsOnIndividual (numportals*2, qfalse, PortalFlow);
    263 #else
    264 	RunThreadsOnIndividual (numportals*2, qtrue, PortalFlow);
    265 #endif
    266 
    267 }
    268 
    269 /*
    270 ==================
    271 CalcPassageVis
    272 ==================
    273 */
    274 void CalcPassageVis(void)
    275 {
    276 	PassageMemory();
    277 
    278 #ifdef MREDEBUG
    279 	_printf("%6d portals out of %d", 0, numportals*2);
    280 	RunThreadsOnIndividual (numportals*2, qfalse, CreatePassages);
    281 	_printf("\n");
    282 	_printf("%6d portals out of %d", 0, numportals*2);
    283 	RunThreadsOnIndividual (numportals*2, qfalse, PassageFlow);
    284 	_printf("\n");
    285 #else
    286 	RunThreadsOnIndividual (numportals*2, qtrue, CreatePassages);
    287 	RunThreadsOnIndividual (numportals*2, qtrue, PassageFlow);
    288 #endif
    289 }
    290 
    291 /*
    292 ==================
    293 CalcPassagePortalVis
    294 ==================
    295 */
    296 void CalcPassagePortalVis(void)
    297 {
    298 	PassageMemory();
    299 
    300 #ifdef MREDEBUG
    301 	_printf("%6d portals out of %d", 0, numportals*2);
    302 	RunThreadsOnIndividual (numportals*2, qfalse, CreatePassages);
    303 	_printf("\n");
    304 	_printf("%6d portals out of %d", 0, numportals*2);
    305 	RunThreadsOnIndividual (numportals*2, qfalse, PassagePortalFlow);
    306 	_printf("\n");
    307 #else
    308 	RunThreadsOnIndividual (numportals*2, qtrue, CreatePassages);
    309 	RunThreadsOnIndividual (numportals*2, qtrue, PassagePortalFlow);
    310 #endif
    311 }
    312 
    313 /*
    314 ==================
    315 CalcFastVis
    316 ==================
    317 */
    318 void CalcFastVis(void)
    319 {
    320 	int		i;
    321 
    322 	// fastvis just uses mightsee for a very loose bound
    323 	for (i=0 ; i<numportals*2 ; i++)
    324 	{
    325 		portals[i].portalvis = portals[i].portalflood;
    326 		portals[i].status = stat_done;
    327 	}
    328 }
    329 
    330 /*
    331 ==================
    332 CalcVis
    333 ==================
    334 */
    335 void CalcVis (void)
    336 {
    337 	int		i;
    338 
    339 	RunThreadsOnIndividual (numportals*2, qtrue, BasePortalVis);
    340 
    341 //	RunThreadsOnIndividual (numportals*2, qtrue, BetterPortalVis);
    342 
    343 	SortPortals ();
    344 
    345 	if (fastvis) {
    346 		CalcFastVis();
    347 	}
    348 	else if ( noPassageVis ) {
    349 		CalcPortalVis();
    350 	}
    351 	else if ( passageVisOnly ) {
    352 		CalcPassageVis();
    353 	}
    354 	else {
    355 		CalcPassagePortalVis();
    356 	}
    357 	//
    358 	// assemble the leaf vis lists by oring and compressing the portal lists
    359 	//
    360 	_printf("creating leaf vis...\n");
    361 	for (i=0 ; i<portalclusters ; i++)
    362 		ClusterMerge (i);
    363 
    364 	_printf( "Total visible clusters: %i\n", totalvis );
    365 	_printf( "Average clusters visible: %i\n", totalvis / portalclusters );
    366 }
    367 
    368 /*
    369 ==================
    370 SetPortalSphere
    371 ==================
    372 */
    373 void SetPortalSphere (vportal_t *p)
    374 {
    375 	int		i;
    376 	vec3_t	total, dist;
    377 	winding_t	*w;
    378 	float	r, bestr;
    379 
    380 	w = p->winding;
    381 	VectorCopy (vec3_origin, total);
    382 	for (i=0 ; i<w->numpoints ; i++)
    383 	{
    384 		VectorAdd (total, w->points[i], total);
    385 	}
    386 	
    387 	for (i=0 ; i<3 ; i++)
    388 		total[i] /= w->numpoints;
    389 
    390 	bestr = 0;		
    391 	for (i=0 ; i<w->numpoints ; i++)
    392 	{
    393 		VectorSubtract (w->points[i], total, dist);
    394 		r = VectorLength (dist);
    395 		if (r > bestr)
    396 			bestr = r;
    397 	}
    398 	VectorCopy (total, p->origin);
    399 	p->radius = bestr;
    400 }
    401 
    402 /*
    403 =============
    404 Winding_PlanesConcave
    405 =============
    406 */
    407 #define WCONVEX_EPSILON		0.2
    408 
    409 int Winding_PlanesConcave(winding_t *w1, winding_t *w2,
    410 							 vec3_t normal1, vec3_t normal2,
    411 							 float dist1, float dist2)
    412 {
    413 	int i;
    414 
    415 	if (!w1 || !w2) return qfalse;
    416 
    417 	// check if one of the points of winding 1 is at the front of the plane of winding 2
    418 	for (i = 0; i < w1->numpoints; i++)
    419 	{
    420 		if (DotProduct(normal2, w1->points[i]) - dist2 > WCONVEX_EPSILON) return qtrue;
    421 	}
    422 	// check if one of the points of winding 2 is at the front of the plane of winding 1
    423 	for (i = 0; i < w2->numpoints; i++)
    424 	{
    425 		if (DotProduct(normal1, w2->points[i]) - dist1 > WCONVEX_EPSILON) return qtrue;
    426 	}
    427 
    428 	return qfalse;
    429 }
    430 
    431 /*
    432 ============
    433 TryMergeLeaves
    434 ============
    435 */
    436 int TryMergeLeaves(int l1num, int l2num)
    437 {
    438 	int i, j, k, n, numportals;
    439 	plane_t plane1, plane2;
    440 	leaf_t *l1, *l2;
    441 	vportal_t *p1, *p2;
    442 	vportal_t *portals[MAX_PORTALS_ON_LEAF];
    443 
    444 	for (k = 0; k < 2; k++)
    445 	{
    446 		if (k) l1 = &leafs[l1num];
    447 		else l1 = &faceleafs[l1num];
    448 		for (i = 0; i < l1->numportals; i++)
    449 		{
    450 			p1 = l1->portals[i];
    451 			if (p1->leaf == l2num) continue;
    452 			for (n = 0; n < 2; n++)
    453 			{
    454 				if (n) l2 = &leafs[l2num];
    455 				else l2 = &faceleafs[l2num];
    456 				for (j = 0; j < l2->numportals; j++)
    457 				{
    458 					p2 = l2->portals[j];
    459 					if (p2->leaf == l1num) continue;
    460 					//
    461 					plane1 = p1->plane;
    462 					plane2 = p2->plane;
    463 					if (Winding_PlanesConcave(p1->winding, p2->winding, plane1.normal, plane2.normal, plane1.dist, plane2.dist))
    464 						return qfalse;
    465 				}
    466 			}
    467 		}
    468 	}
    469 	for (k = 0; k < 2; k++)
    470 	{
    471 		if (k)
    472 		{
    473 			l1 = &leafs[l1num];
    474 			l2 = &leafs[l2num];
    475 		}
    476 		else
    477 		{
    478 			l1 = &faceleafs[l1num];
    479 			l2 = &faceleafs[l2num];
    480 		}
    481 		numportals = 0;
    482 		//the leaves can be merged now
    483 		for (i = 0; i < l1->numportals; i++)
    484 		{
    485 			p1 = l1->portals[i];
    486 			if (p1->leaf == l2num)
    487 			{
    488 				p1->removed = qtrue;
    489 				continue;
    490 			}
    491 			portals[numportals++] = p1;
    492 		}
    493 		for (j = 0; j < l2->numportals; j++)
    494 		{
    495 			p2 = l2->portals[j];
    496 			if (p2->leaf == l1num)
    497 			{
    498 				p2->removed = qtrue;
    499 				continue;
    500 			}
    501 			portals[numportals++] = p2;
    502 		}
    503 		for (i = 0; i < numportals; i++)
    504 		{
    505 			l2->portals[i] = portals[i];
    506 		}
    507 		l2->numportals = numportals;
    508 		l1->merged = l2num;
    509 	}
    510 	return qtrue;
    511 }
    512 
    513 /*
    514 ============
    515 UpdatePortals
    516 ============
    517 */
    518 void UpdatePortals(void)
    519 {
    520 	int i;
    521 	vportal_t *p;
    522 
    523 	for (i = 0; i < numportals * 2; i++)
    524 	{
    525 		p = &portals[i];
    526 		if (p->removed)
    527 			continue;
    528 		while(leafs[p->leaf].merged >= 0)
    529 			p->leaf = leafs[p->leaf].merged;
    530 	}
    531 }
    532 
    533 /*
    534 ============
    535 MergeLeaves
    536 
    537 try to merge leaves but don't merge through hint splitters
    538 ============
    539 */
    540 void MergeLeaves(void)
    541 {
    542 	int i, j, nummerges, totalnummerges;
    543 	leaf_t *leaf;
    544 	vportal_t *p;
    545 
    546 	totalnummerges = 0;
    547 	do
    548 	{
    549 		nummerges = 0;
    550 		for (i = 0; i < portalclusters; i++)
    551 		{
    552 			leaf = &leafs[i];
    553 			//if this leaf is merged already
    554 			if (leaf->merged >= 0)
    555 				continue;
    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 	_printf("%6d leaves merged\n", totalnummerges);
    577 }
    578 
    579 /*
    580 ============
    581 TryMergeWinding
    582 ============
    583 */
    584 #define	CONTINUOUS_EPSILON	0.005
    585 
    586 winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
    587 {
    588 	vec_t		*p1, *p2, *p3, *p4, *back;
    589 	winding_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 = NewWinding (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 	winding_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 						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 	_printf("%6d portals merged\n", nummerges);
    733 	_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 	_printf("%6d active portals\n", num);
    759 	_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 	winding_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 	winding_t	*w;
    854 	int			leafnums[2];
    855 	plane_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 	_printf ("%6i portalclusters\n", portalclusters);
    872 	_printf ("%6i numportals\n", numportals);
    873 	_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 = malloc(2*numportals*sizeof(vportal_t));
    884 	memset (portals, 0, 2*numportals*sizeof(vportal_t));
    885 	
    886 	leafs = 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 	numVisBytes = VIS_HEADER_SIZE + portalclusters*leafbytes;
    893 
    894 	((int *)visBytes)[0] = portalclusters;
    895 	((int *)visBytes)[1] = leafbytes;
    896 		
    897 	for (i=0, p=portals ; i<numportals ; i++)
    898 	{
    899 		if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)
    900 			Error ("LoadPortals: reading portal %i", i);
    901 		if (numpoints > MAX_POINTS_ON_WINDING)
    902 			Error ("LoadPortals: portal %i has too many points", i);
    903 		if ( (unsigned)leafnums[0] > portalclusters
    904 		|| (unsigned)leafnums[1] > portalclusters)
    905 			Error ("LoadPortals: reading portal %i", i);
    906 		if (fscanf (f, "%i ", &hint) != 1)
    907 			Error ("LoadPortals: reading hint state");
    908 		
    909 		w = p->winding = NewWinding (numpoints);
    910 		w->numpoints = numpoints;
    911 		
    912 		for (j=0 ; j<numpoints ; j++)
    913 		{
    914 			double	v[3];
    915 			int		k;
    916 
    917 			// scanf into double, then assign to vec_t
    918 			// so we don't care what size vec_t is
    919 			if (fscanf (f, "(%lf %lf %lf ) "
    920 			, &v[0], &v[1], &v[2]) != 3)
    921 				Error ("LoadPortals: reading portal %i", i);
    922 			for (k=0 ; k<3 ; k++)
    923 				w->points[j][k] = v[k];
    924 		}
    925 		fscanf (f, "\n");
    926 		
    927 		// calc plane
    928 		PlaneFromWinding (w, &plane);
    929 
    930 		// create forward portal
    931 		l = &leafs[leafnums[0]];
    932 		if (l->numportals == MAX_PORTALS_ON_LEAF)
    933 			Error ("Leaf with too many portals");
    934 		l->portals[l->numportals] = p;
    935 		l->numportals++;
    936 		
    937 		p->num = i+1;
    938 		p->hint = hint;
    939 		p->winding = w;
    940 		VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
    941 		p->plane.dist = -plane.dist;
    942 		p->leaf = leafnums[1];
    943 		SetPortalSphere (p);
    944 		p++;
    945 		
    946 		// create backwards portal
    947 		l = &leafs[leafnums[1]];
    948 		if (l->numportals == MAX_PORTALS_ON_LEAF)
    949 			Error ("Leaf with too many portals");
    950 		l->portals[l->numportals] = p;
    951 		l->numportals++;
    952 		
    953 		p->num = i+1;
    954 		p->hint = hint;
    955 		p->winding = NewWinding(w->numpoints);
    956 		p->winding->numpoints = w->numpoints;
    957 		for (j=0 ; j<w->numpoints ; j++)
    958 		{
    959 			VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
    960 		}
    961 
    962 		p->plane = plane;
    963 		p->leaf = leafnums[0];
    964 		SetPortalSphere (p);
    965 		p++;
    966 
    967 	}
    968 
    969 	faces = malloc(2*numfaces*sizeof(vportal_t));
    970 	memset (faces, 0, 2*numfaces*sizeof(vportal_t));
    971 
    972 	faceleafs = malloc(portalclusters*sizeof(leaf_t));
    973 	memset(faceleafs, 0, portalclusters*sizeof(leaf_t));
    974 
    975 	for (i = 0, p = faces; i < numfaces; i++)
    976 	{
    977 		if (fscanf (f, "%i %i ", &numpoints, &leafnums[0]) != 2)
    978 			Error ("LoadPortals: reading portal %i", i);
    979 
    980 		w = p->winding = NewWinding (numpoints);
    981 		w->numpoints = numpoints;
    982 		
    983 		for (j=0 ; j<numpoints ; j++)
    984 		{
    985 			double	v[3];
    986 			int		k;
    987 
    988 			// scanf into double, then assign to vec_t
    989 			// so we don't care what size vec_t is
    990 			if (fscanf (f, "(%lf %lf %lf ) "
    991 			, &v[0], &v[1], &v[2]) != 3)
    992 				Error ("LoadPortals: reading portal %i", i);
    993 			for (k=0 ; k<3 ; k++)
    994 				w->points[j][k] = v[k];
    995 		}
    996 		fscanf (f, "\n");
    997 		
    998 		// calc plane
    999 		PlaneFromWinding (w, &plane);
   1000 
   1001 		l = &faceleafs[leafnums[0]];
   1002 		l->merged = -1;
   1003 		if (l->numportals == MAX_PORTALS_ON_LEAF)
   1004 			Error ("Leaf with too many faces");
   1005 		l->portals[l->numportals] = p;
   1006 		l->numportals++;
   1007 		
   1008 		p->num = i+1;
   1009 		p->winding = w;
   1010 		// normal pointing out of the leaf
   1011 		VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
   1012 		p->plane.dist = -plane.dist;
   1013 		p->leaf = -1;
   1014 		SetPortalSphere (p);
   1015 		p++;
   1016 	}
   1017 	
   1018 	fclose (f);
   1019 }
   1020 
   1021 
   1022 /*
   1023 ================
   1024 CalcPHS
   1025 
   1026 Calculate the PHS (Potentially Hearable Set)
   1027 by ORing together all the PVS visible from a leaf
   1028 ================
   1029 */
   1030 void CalcPHS (void)
   1031 {
   1032 	int		i, j, k, l, index;
   1033 	int		bitbyte;
   1034 	long	*dest, *src;
   1035 	byte	*scan;
   1036 	int		count;
   1037 	byte	uncompressed[MAX_MAP_LEAFS/8];
   1038 
   1039 	_printf ("Building PHS...\n");
   1040 
   1041 	count = 0;
   1042 	for (i=0 ; i<portalclusters ; i++)
   1043 	{
   1044 		scan = visBytes + i*leafbytes;
   1045 		memcpy (uncompressed, scan, leafbytes);
   1046 		for (j=0 ; j<leafbytes ; j++)
   1047 		{
   1048 			bitbyte = scan[j];
   1049 			if (!bitbyte)
   1050 				continue;
   1051 			for (k=0 ; k<8 ; k++)
   1052 			{
   1053 				if (! (bitbyte & (1<<k)) )
   1054 					continue;
   1055 				// OR this pvs row into the phs
   1056 				index = ((j<<3)+k);
   1057 				if (index >= portalclusters)
   1058 					Error ("Bad bit in PVS");	// pad bits should be 0
   1059 				src = (long *)(visBytes + index*leafbytes);
   1060 				dest = (long *)uncompressed;
   1061 				for (l=0 ; l<leaflongs ; l++)
   1062 					((long *)uncompressed)[l] |= src[l];
   1063 			}
   1064 		}
   1065 		for (j=0 ; j<portalclusters ; j++)
   1066 			if (uncompressed[j>>3] & (1<<(j&7)) )
   1067 				count++;
   1068 
   1069 		// FIXME: copy it off
   1070 	}
   1071 
   1072 	_printf ("Average clusters hearable: %i\n", count/portalclusters);
   1073 }
   1074 
   1075 /*
   1076 ===========
   1077 VisMain
   1078 ===========
   1079 */
   1080 int VisMain (int argc, char **argv)
   1081 {
   1082 	char		portalfile[1024];
   1083 	char		name[1024];
   1084 	int		i;
   1085 	double		start, end;
   1086 		
   1087 	_printf ("---- vis ----\n");
   1088 
   1089 	verbose = qfalse;
   1090 	for (i=1 ; i<argc ; i++) {
   1091 		if (!strcmp(argv[i],"-threads")) {
   1092 			numthreads = atoi (argv[i+1]);
   1093 			i++;
   1094 		} else if (!strcmp(argv[i],"-threads")) {
   1095 			numthreads = atoi (argv[i+1]);
   1096 			i++;
   1097 		} else if (!strcmp(argv[i], "-fast")) {
   1098 			_printf ("fastvis = true\n");
   1099 			fastvis = qtrue;
   1100 		} else if (!strcmp(argv[i], "-merge")) {
   1101 			_printf ("merge = true\n");
   1102 			mergevis = qtrue;
   1103 		} else if (!strcmp(argv[i], "-nopassage")) {
   1104 			_printf ("nopassage = true\n");
   1105 			noPassageVis = qtrue;
   1106 		} else if (!strcmp(argv[i], "-passageOnly")) {
   1107 			_printf("passageOnly = true\n");
   1108 			passageVisOnly = qtrue;
   1109 		} else if (!strcmp(argv[i], "-level")) {
   1110 			testlevel = atoi(argv[i+1]);
   1111 			_printf ("testlevel = %i\n", testlevel);
   1112 			i++;
   1113 		} else if (!strcmp(argv[i], "-v")) {
   1114 			_printf ("verbose = true\n");
   1115 			verbose = qtrue;
   1116 		} else if (!strcmp (argv[i],"-nosort")) {
   1117 			_printf ("nosort = true\n");
   1118 			nosort = qtrue;
   1119 		} else if (!strcmp (argv[i],"-saveprt")) {
   1120 			_printf ("saveprt = true\n");
   1121 			saveprt = qtrue;
   1122 		} else if (!strcmp (argv[i],"-tmpin")) {
   1123 			strcpy (inbase, "/tmp");
   1124 		} else if (!strcmp (argv[i],"-tmpout")) {
   1125 			strcpy (outbase, "/tmp");
   1126 		} else if (argv[i][0] == '-') {
   1127 			Error ("Unknown option \"%s\"", argv[i]);
   1128 		} else {
   1129 			break;
   1130 		}
   1131 	}
   1132 
   1133 	if (i != argc - 1)
   1134 		Error ("usage: vis [-threads #] [-level 0-4] [-fast] [-v] bspfile");
   1135 
   1136 #ifdef MREDEBUG
   1137 	start = clock();
   1138 #else
   1139 	start = I_FloatTime ();
   1140 #endif
   1141 	
   1142 	ThreadSetDefault ();
   1143 
   1144 	SetQdirFromPath (argv[i]);	
   1145 
   1146 #ifdef _WIN32
   1147   InitPakFile(gamedir, NULL);
   1148 #endif
   1149 
   1150 	// load the bsp
   1151 	sprintf (name, "%s%s", inbase, ExpandArg(argv[i]));
   1152 	StripExtension (name);
   1153 	strcat (name, ".bsp");
   1154 	_printf ("reading %s\n", name);
   1155 	LoadBSPFile (name);
   1156 
   1157 	// load the portal file
   1158 	sprintf (portalfile, "%s%s", inbase, ExpandArg(argv[i]));
   1159 	StripExtension (portalfile);
   1160 	strcat (portalfile, ".prt");
   1161 	_printf ("reading %s\n", portalfile);
   1162 	LoadPortals (portalfile);
   1163 
   1164 	if (mergevis)
   1165 	{
   1166 		MergeLeaves();
   1167 		MergeLeafPortals();
   1168 	}
   1169 
   1170 	CountActivePortals();
   1171 //	WritePortals("maps/hints.prs");
   1172 
   1173 	_printf ("visdatasize:%i\n", numVisBytes);
   1174 
   1175 	CalcVis ();
   1176 
   1177 //	CalcPHS ();
   1178 
   1179 	// delete the prt file
   1180 	if ( !saveprt ) {
   1181 		remove( portalfile );
   1182 	}
   1183 
   1184 	// write the bsp file
   1185 	_printf ("writing %s\n", name);
   1186 	WriteBSPFile (name);
   1187 
   1188 #ifdef MREDEBUG
   1189 	end = clock();
   1190 	_printf ("%5.2f seconds elapsed\n", (end-start) / CLK_TCK);
   1191 #else
   1192 	end = I_FloatTime ();
   1193 	_printf ("%5.2f seconds elapsed\n", end-start);
   1194 #endif
   1195 	return 0;
   1196 }
   1197