// vis.c

#include "vis.h"

#define MAX_THREADS             4

int GilMode=1;

int                     numportals;
int                     portalleafs;

portal_t        *portals;
leaf_t          *leafs;

int                     c_portaltest, c_portalpass, c_portalcheck;


qboolean                showgetleaf = true;

int             leafon;                 // the next leaf to be given to a thread to process

#ifdef __alpha
#ifdef _WIN32
HANDLE my_mutex;
#else
pthread_mutex_t *my_mutex;
#endif  //_win32
#endif
//alpha

byte    *vismap, *vismap_p, *vismap_end;        // past visfile
int             originalvismapsize;

byte    *uncompressed;                  // [bitbytes*portalleafs]

int             bitbytes;                               // (portalleafs+63)>>3
int             bitlongs;

#ifdef __alpha
int                     numthreads = 4;
#else
int                     numthreads = 1;
#endif

qboolean                fastvis;
qboolean                verbose;
int                     testlevel = 2;

#if 0
void NormalizePlane (plane_t *dp)
{
	double  ax, ay, az;

	if (dp->normal[0] == -1.0)
	{
		dp->normal[0] = 1.0;
		dp->dist = -dp->dist;
		return;
	}
	if (dp->normal[1] == -1.0)
	{
		dp->normal[1] = 1.0;
		dp->dist = -dp->dist;
		return;
	}
	if (dp->normal[2] == -1.0)
	{
		dp->normal[2] = 1.0;
		dp->dist = -dp->dist;
		return;
	}

	ax = fabs(dp->normal[0]);
	ay = fabs(dp->normal[1]);
	az = fabs(dp->normal[2]);

	if (ax >= ay && ax >= az)
	{
		if (dp->normal[0] < 0)
		{
			VectorSubtract (vec3_origin, dp->normal, dp->normal);
			dp->dist = -dp->dist;
		}
		return;
	}

	if (ay >= ax && ay >= az)
	{
		if (dp->normal[1] < 0)
		{
			VectorSubtract (vec3_origin, dp->normal, dp->normal);
			dp->dist = -dp->dist;
		}
		return;
	}

	if (dp->normal[2] < 0)
	{
		VectorSubtract (vec3_origin, dp->normal, dp->normal);
		dp->dist = -dp->dist;
	}
}
#endif

void PlaneFromWinding (winding_t *w, plane_t *plane)
{
	vec3_t          v1, v2;

// calc plane
	VectorSubtract (w->points[2], w->points[1], v1);
	VectorSubtract (w->points[0], w->points[1], v2);
	CrossProduct (v2, v1, plane->normal);
	VectorNormalize (plane->normal);
	plane->dist = (float) DotProduct (w->points[0], plane->normal);
}

//=============================================================================

/*
==================
NewWinding
==================
*/


#define MAX_FREE_WINDINGS (10000)
#define MAX_FAST_ALLOC_WINDINGS (16)

#ifdef _WIN32
winding_t *OldNewWinding (int points)
{
	winding_t       *w;
	int             size;

	if (points > MAX_POINTS_ON_WINDING)
		Error ("NewWinding: %i points", points);

	size = (int)((winding_t *)0)->points[points];
	w = malloc (size);
//    memset (w, 0, size);
	w->fixedsize=0;
    w->original=0;
    w->numpoints=0;

	return w;
}

void OldFreeWinding (winding_t *w)
{
	if (!w->original)
		free (w);
}

__declspec( thread ) static int nFreeWindings=0;
__declspec( thread ) static winding_t *FreeWindings[MAX_FREE_WINDINGS];


winding_t *NewWinding (int points)
{
    winding_t *ret;
    if (points>MAX_FAST_ALLOC_WINDINGS||!GilMode)
	{
	ret=OldNewWinding(points);
		ret->fixedsize=0;
	}
    else if (nFreeWindings)
	ret=FreeWindings[--nFreeWindings];
    else
	{
	ret=OldNewWinding(MAX_FAST_ALLOC_WINDINGS);
		ret->fixedsize=1;
	}
    ret->original=0;
    ret->numpoints=0;
    return ret;
}

void FreeWinding (winding_t *w)
{
	if (!w->original)
    {
	if (nFreeWindings>=MAX_FREE_WINDINGS||!w->fixedsize)
	    OldFreeWinding(w);
	else
	    FreeWindings[nFreeWindings++]=w;
    }
}
#else   //old way
winding_t *NewWinding (int points)
{
	winding_t       *w;
	int                     size;
	
	if (points > MAX_POINTS_ON_WINDING)
		Error ("NewWinding: %i points", points);
	
	size = (int)((winding_t *)0)->points[points];
	w = malloc (size);
	memset (w, 0, size);
	
	return w;
}


void FreeWinding (winding_t *w)
{
	if (!w->original)
		free (w);
}
#endif


void pw(winding_t *w)
{
	int             i;
	for (i=0 ; i<w->numpoints ; i++)
		printf ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
}

void prl(leaf_t *l)
{
	int                     i;
	portal_t        *p;
	plane_t         pl;

	for (i=0 ; i<l->numportals ; i++)
	{
		p = l->portals[i];
		pl = p->plane;
		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]);
	}
}

/*
==================
CopyWinding
==================
*/
winding_t       *CopyWinding (winding_t *w)
{
	int                     size;
	winding_t       *c;
	size = (int)((winding_t *)0)->points[w->numpoints];
/*
	c = malloc (size);
*/
	c = NewWinding(w->numpoints);
	memcpy (c, w, size);
	c->original = false;
	return c;
}


/*
==================
ClipWinding

Clips the winding to the plane, returning the new winding on the positive side
Frees the input winding.
If keepon is true, an exactly on-plane winding will be saved, otherwise
it will be clipped away.
==================
*/
winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
{
	double  dists[MAX_POINTS_ON_WINDING];
	int             sides[MAX_POINTS_ON_WINDING];
	int             counts[3];
	double  dot;
	int             i, j;
	double  *p1, *p2;
	vec3_t  mid;
	winding_t       *neww;
	int             maxpts;

	counts[0] = counts[1] = counts[2] = 0;

// determine sides for each point
	for (i=0 ; i<in->numpoints ; i++)
	{
		dot = DotProduct (in->points[i], split->normal);
		dot -= split->dist;
		dists[i] = dot;
		if (dot > ON_EPSILON)
			sides[i] = SIDE_FRONT;
		else if (dot < -ON_EPSILON)
			sides[i] = SIDE_BACK;
		else
		{
			sides[i] = SIDE_ON;
		}
		counts[sides[i]]++;
	}
	sides[i] = sides[0];
	dists[i] = dists[0];

	if (keepon && !counts[0] && !counts[1])
		return in;

	if (!counts[0])
	{
		FreeWinding (in);
		return NULL;
	}
	if (!counts[1])
		return in;

	maxpts = in->numpoints+4;       // can't use counts[0]+2 because
								// of fp grouping errors
	neww = NewWinding (maxpts);

	for (i=0 ; i<in->numpoints ; i++)
	{
		p1 = in->points[i];

		if (sides[i] == SIDE_ON)
		{
			VectorCopy (p1, neww->points[neww->numpoints]);
			neww->numpoints++;
			continue;
		}

		if (sides[i] == SIDE_FRONT)
		{
			VectorCopy (p1, neww->points[neww->numpoints]);
			neww->numpoints++;
		}

		if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
			continue;

	// generate a split point
		p2 = in->points[(i+1)%in->numpoints];

		dot = dists[i] / (dists[i]-dists[i+1]);
		for (j=0 ; j<3 ; j++)
		{       // avoid round off error when possible
			if (split->normal[j] == 1)
				mid[j] = split->dist;
			else if (split->normal[j] == -1)
				mid[j] = -split->dist;
			else
				mid[j] = p1[j] + dot*(p2[j]-p1[j]);
		}

		VectorCopy (mid, neww->points[neww->numpoints]);
		neww->numpoints++;
	}

	if (neww->numpoints > maxpts)
		Error ("ClipWinding: points exceeded estimate");

// free the original winding
	FreeWinding (in);

	return neww;
}


//=============================================================================

/*
=============
GetNextPortal

Returns the next portal for a thread to work on
Returns the portals from the least complex, so the later ones can reuse
the earlier information.
=============
*/
#if 1
//static int maxpercent=0;
portal_t *GetNextPortal (void)
{
	int             j;
	portal_t        *p, *tp;
    int		min;
	//int		ndone;

	const int       num2 = numportals*2;
	
	min = 99999;
	p = NULL;

	LOCK;

//	ndone=0;
	for (j=0, tp = portals ; j<num2 ; j++, tp++)
	{
		if (tp->nummightsee < min && tp->status == stat_none)
		{
			min = tp->nummightsee;
			p = tp;
		}
/*		else if (tp->status == stat_done)
			ndone++;
*/
	}

	if (p)
		p->status = stat_working;

	UNLOCK;
/*
   if (GilMode)
	{
		ndone*=50;
		ndone/=numportals;
		if (ndone-1>maxpercent)
		{
			maxpercent=ndone;
			printf("%d %% done\t",ndone);
		}
	}
*/      

	return p;
}
#else
portal_t *GetNextPortal (void)
{
	int             j;
	portal_t        *p, *tp;
	int             min;

	LOCK;

    min = -99999;
	p = NULL;

	for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
	{
	if (tp->nummightsee > min && tp->status == stat_none)
		{
			min = tp->nummightsee;
			p = tp;
		}
	}


	if (p)
		p->status = stat_working;

	UNLOCK;

	return p;
}
#endif
/*
==============
LeafThread
==============
*/
#ifdef __alpha
#ifdef _WIN32
LPVOID LeafThread (LPVOID thread)
#else
pthread_addr_t LeafThread (pthread_addr_t thread)
#endif
#else
void *LeafThread (int thread)
#endif
{
	portal_t        *p;

printf ("Begining LeafThread: %i\n",(int)thread);
	do
	{
		p = GetNextPortal ();
		if (!p)
			break;

		PortalFlow (p);

		if (verbose)
			printf ("portal:%4i  mightsee:%4i  cansee:%4i\n", (int)(p - portals), p->nummightsee, p->numcansee);
	} while (1);

printf ("Completed LeafThread: %i\n",(int)thread);

	return NULL;
}

/*
===============
CompressRow

===============
*/
int CompressRow (byte *vis, byte *dest)
{
	int             j;
	int             rep;
	int             visrow;
	byte    *dest_p;

	dest_p = dest;
	visrow = (portalleafs + 7)>>3;

	for (j=0 ; j<visrow ; j++)
	{
		*dest_p++ = vis[j];
		if (vis[j])
			continue;

		rep = 1;
		for ( j++; j<visrow ; j++)
			if (vis[j] || rep == 255)
				break;
			else
				rep++;
		*dest_p++ = rep;
		j--;
	}

	return dest_p - dest;
}


/*
===============
LeafFlow

Builds the entire visibility list for a leaf
===============
*/
int             totalvis;

void LeafFlow (int leafnum)
{
	leaf_t          *leaf;
	byte            *outbuffer;
	byte            compressed[MAX_MAP_LEAFS/8];
	int                     i, j;
	int                     numvis;
	byte            *dest;
	portal_t        *p;

//
// flow through all portals, collecting visible bits
//
	outbuffer = uncompressed + leafnum*bitbytes;
	leaf = &leafs[leafnum];
	for (i=0 ; i<leaf->numportals ; i++)
	{
		p = leaf->portals[i];
		if (p->status != stat_done)
			Error ("portal not done");
		for (j=0 ; j<bitbytes ; j++)
			outbuffer[j] |= p->visbits[j];
	}

	if (outbuffer[leafnum>>3] & (1<<(leafnum&7)))
		Error ("Leaf portals saw into leaf");

	outbuffer[leafnum>>3] |= (1<<(leafnum&7));

	numvis = 0;
	for (i=0 ; i<portalleafs ; i++)
		if (outbuffer[i>>3] & (1<<(i&3)))
			numvis++;

//
// compress the bit string
//
	if (verbose)
		printf ("leaf %4i : %4i visible\n", leafnum, numvis);
	totalvis += numvis;

#if 0
	i = (portalleafs+7)>>3;
	memcpy (compressed, outbuffer, i);
#else
	i = CompressRow (outbuffer, compressed);
#endif

	dest = vismap_p;
	vismap_p += i;

	if (vismap_p > vismap_end)
		Error ("Vismap expansion overflow");

	dleafs[leafnum+1].visofs = dest-vismap; // leaf 0 is a common solid

	memcpy (dest, compressed, i);
}


/*
==================
CalcPortalVis
==================
*/
void CalcPortalVis (void)
{
	int             i;

// fastvis just uses mightsee for a very loose bound
	if (fastvis)
	{
		for (i=0 ; i<numportals*2 ; i++)
		{
			portals[i].visbits = portals[i].mightsee;
			portals[i].status = stat_done;
		}
		return;
	}

	leafon = 0;

#ifdef __alpha
#ifdef _WIN32
{
	DWORD IDThread;
	HANDLE work_threads[MAX_THREADS];
	int             i;
	
	my_mutex = CreateMutex(NULL, FALSE, NULL);      //cleared

	for (i=0 ; i<numthreads-1 ; i++)
	{
		work_threads[i] = CreateThread(NULL,	// no security attrib
			0x100000,							// stack size
			(LPTHREAD_START_ROUTINE) LeafThread,// thread function
			(LPVOID) i,					// thread function arg
			0,							// use default creation flags
			&IDThread);
		
		if (work_threads[i] == NULL)
			Error ("pthread_create failed");
	}

	LeafThread((LPVOID)(numthreads-1));
	for (i=0 ; i<numthreads-1 ; i++)
	{
		WaitForSingleObject(work_threads[i], INFINITE);
	}

	CloseHandle (my_mutex);
}
#else
{
	pthread_t       work_threads[MAX_THREADS];
	pthread_addr_t  status;
	pthread_attr_t  attrib;
	pthread_mutexattr_t     mattrib;
	int             i;

	my_mutex = malloc (sizeof(*my_mutex));
	if (pthread_mutexattr_create (&mattrib) == -1)
		Error ("pthread_mutex_attr_create failed");
	if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
		Error ("pthread_mutexattr_setkind_np failed");
	if (pthread_mutex_init (my_mutex, mattrib) == -1)
		Error ("pthread_mutex_init failed");

	if (pthread_attr_create (&attrib) == -1)
		Error ("pthread_attr_create failed");
	if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
		Error ("pthread_attr_setstacksize failed");

	for (i=0 ; i<numthreads ; i++)
	{
		if (pthread_create(&work_threads[i], attrib
		, LeafThread, (pthread_addr_t)i) == -1)
			Error ("pthread_create failed");
	}

	for (i=0 ; i<numthreads ; i++)
	{
		if (pthread_join (work_threads[i], &status) == -1)
			Error ("pthread_join failed");
	}

	if (pthread_mutex_destroy (my_mutex) == -1)
			Error ("pthread_mutex_destroy failed");
}
#endif  // _win32
#else
	LeafThread (0);
#endif  // __alpha

	if (verbose)
	{
		printf ("portalcheck: %i  portaltest: %i  portalpass: %i\n",c_portalcheck, c_portaltest, c_portalpass);
		printf ("c_vistest: %i  c_mighttest: %i\n",c_vistest, c_mighttest);
	}

}


/*
==================
CalcVis
==================
*/
void CalcVis (void)
{
	int             i;

	BasePortalVis ();

	CalcPortalVis ();

//
// assemble the leaf vis lists by oring and compressing the portal lists
//
	for (i=0 ; i<portalleafs ; i++)
		LeafFlow (i);

	printf ("average leafs visible: %i\n", totalvis / portalleafs);
}

/*
==============================================================================

PASSAGE CALCULATION (not used yet...)

==============================================================================
*/

int             count_sep;

qboolean PlaneCompare (plane_t *p1, plane_t *p2)
{
	int             i;

	if ( fabs(p1->dist - p2->dist) > 0.01)
		return false;

	for (i=0 ; i<3 ; i++)
		if ( fabs(p1->normal[i] - p2->normal[i] ) > 0.001)
			return false;

	return true;
}

sep_t   *Findpassages (winding_t *source, winding_t *pass)
{
	int                     i, j, k, l;
	plane_t         plane;
	vec3_t          v1, v2;
	float           d;
	double          length;
	int                     counts[3];
	qboolean                fliptest;
	sep_t           *sep, *list;

	list = NULL;

// check all combinations
	for (i=0 ; i<source->numpoints ; i++)
	{
		l = (i+1)%source->numpoints;
		VectorSubtract (source->points[l] , source->points[i], v1);

	// fing a vertex of pass that makes a plane that puts all of the
	// vertexes of pass on the front side and all of the vertexes of
	// source on the back side
		for (j=0 ; j<pass->numpoints ; j++)
		{
			VectorSubtract (pass->points[j], source->points[i], v2);

			plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
			plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
			plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];

		// if points don't make a valid plane, skip it

			length = plane.normal[0] * plane.normal[0]
			+ plane.normal[1] * plane.normal[1]
			+ plane.normal[2] * plane.normal[2];

			if (length < ON_EPSILON)
				continue;

			length = 1/sqrt(length);

			plane.normal[0] *= length;
			plane.normal[1] *= length;
			plane.normal[2] *= length;

			plane.dist = (float) DotProduct (pass->points[j], plane.normal);

		//
		// find out which side of the generated seperating plane has the
		// source portal
		//
			fliptest = false;
			for (k=0 ; k<source->numpoints ; k++)
			{
				if (k == i || k == l)
					continue;
				d = (float) DotProduct (source->points[k], plane.normal) - plane.dist;
				if (d < -ON_EPSILON)
				{       // source is on the negative side, so we want all
					// pass and target on the positive side
					fliptest = false;
					break;
				}
				else if (d > ON_EPSILON)
				{       // source is on the positive side, so we want all
					// pass and target on the negative side
					fliptest = true;
					break;
				}
			}
			if (k == source->numpoints)
				continue;               // planar with source portal

		//
		// flip the normal if the source portal is backwards
		//
			if (fliptest)
			{
				VectorSubtract (vec3_origin, plane.normal, plane.normal);
				plane.dist = -plane.dist;
			}

		//
		// if all of the pass portal points are now on the positive side,
		// this is the seperating plane
		//
			counts[0] = counts[1] = counts[2] = 0;
			for (k=0 ; k<pass->numpoints ; k++)
			{
				if (k==j)
					continue;
				d = (float) DotProduct (pass->points[k], plane.normal) - plane.dist;
				if (d < -ON_EPSILON)
					break;
				else if (d > ON_EPSILON)
					counts[0]++;
				else
					counts[2]++;
			}
			if (k != pass->numpoints)
				continue;       // points on negative side, not a seperating plane

			if (!counts[0])
				continue;       // planar with pass portal

		//
		// save this out
		//
			count_sep++;

			sep = malloc(sizeof(*sep));
			sep->next = list;
			list = sep;
			sep->plane = plane;
		}
	}

	return list;
}


//=============================================================================

/*
============
LoadPortals
============
*/
void LoadPortals (char *name)
{
	int                     i, j;
	portal_t        *p;
	leaf_t          *l;
	char            magic[80];
	FILE            *f;
	int                     numpoints;
	winding_t       *w;
	int                     leafnums[2];
	plane_t         plane;

	if (!strcmp(name,"-"))
		f = stdin;
	else
	{
		f = fopen(name, "r");
		if (!f)
		{
			printf ("LoadPortals: couldn't read %s\n",name);
			printf ("No vising performed.\n");
			exit (1);
		}
	}

	if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalleafs, &numportals) != 3)
		Error ("LoadPortals: failed to read header");
	if (strcmp(magic,PORTALFILE))
		Error ("LoadPortals: not a portal file");

	printf ("%4i portalleafs\n", portalleafs);
	printf ("%4i numportals\n", numportals);

	bitbytes = ((portalleafs+63)&~63)>>3;
	bitlongs = bitbytes/sizeof(long);

// each file portal is split into two memory portals
	portals = malloc(2*numportals*sizeof(portal_t));
	memset (portals, 0, 2*numportals*sizeof(portal_t));

	leafs = malloc(portalleafs*sizeof(leaf_t));
	memset (leafs, 0, portalleafs*sizeof(leaf_t));

	originalvismapsize = portalleafs*((portalleafs+7)/8);

	vismap = vismap_p = dvisdata;
	vismap_end = vismap + MAX_MAP_VISIBILITY;

	for (i=0, p=portals ; i<numportals ; i++)
	{
		if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
			!= 3)
			Error ("LoadPortals: Error reading portal %i", i);
		if (numpoints > MAX_POINTS_ON_WINDING)
			Error ("LoadPortals: portal %i has too many points", i);
		if ( leafnums[0] > portalleafs
		|| leafnums[1] > portalleafs)
			Error ("LoadPortals: portal %i, leafnums > portalleafs", i);

		w = p->winding = NewWinding (numpoints);
		w->original = true;
		w->numpoints = numpoints;

		for (j=0 ; j<numpoints ; j++)
		{
			if (fscanf (f, "(%lf %lf %lf ) "
			, &w->points[j][0], &w->points[j][1], &w->points[j][2]) != 3)
				Error ("LoadPortals: reading portal %i", i);
		}
		fscanf (f, "\n");

	// calc plane
		PlaneFromWinding (w, &plane);

	// create forward portal
		l = &leafs[leafnums[0]];
		if (l->numportals == MAX_PORTALS_ON_LEAF)
			Error ("Leaf with too many portals");
		l->portals[l->numportals] = p;
		l->numportals++;

		p->winding = w;
		VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
		p->plane.dist = -plane.dist;
		p->leaf = leafnums[1];
		p++;

	// create backwards portal
		l = &leafs[leafnums[1]];
		if (l->numportals == MAX_PORTALS_ON_LEAF)
			Error ("Leaf with too many portals");
		l->portals[l->numportals] = p;
		l->numportals++;

		p->winding = w;
		p->plane = plane;
		p->leaf = leafnums[0];
		p++;

	}

	fclose (f);
}


/*
===========
main
===========
*/
int main (int argc, char **argv)
{
	char    portalfile[1024];
	char            source[1024];
	int             i;
	double          start, end;

#ifdef __alpha
#ifdef _WIN32
	printf ("Alpha,Win32---- vis ----\n");
#else        
	printf ("Alpha---- vis ----\n");
#endif
#else        
	printf ("---- vis ----\n");
#endif
	
	for (i=1 ; i<argc ; i++)
	{
		if (!strcmp(argv[i],"-threads"))
		{
			numthreads = atoi (argv[i+1]);
			i++;
		}
	else if (!strcmp(argv[i], "-nogil"))
		{
			printf ("Gil's optimizations disabled\n");
	    GilMode=0;
		}
		else if (!strcmp(argv[i], "-fast"))
		{
			printf ("fastvis = true\n");
			fastvis = true;
		}
		else if (!strcmp(argv[i], "-level"))
		{
			testlevel = atoi(argv[i+1]);
			printf ("testlevel = %i\n", testlevel);
			i++;
		}
		else if (!strcmp(argv[i], "-v"))
		{
			printf ("verbose = true\n");
			verbose = true;
		}
		else if (argv[i][0] == '-')
			Error ("Unknown option \"%s\"", argv[i]);
		else
			break;
	}

	if (i != argc - 1)
		Error ("usage: vis [-nogil] [-threads #] [-level 0-4] [-fast] [-v] bspfile");

	start = I_FloatTime ();

	strcpy (source, argv[i]);
	StripExtension (source);
	DefaultExtension (source, ".bsp");

	LoadBSPFile (source);

	strcpy (portalfile, argv[i]);
	StripExtension (portalfile);
	strcat (portalfile, ".prt");

	LoadPortals (portalfile);

	uncompressed = malloc(bitbytes*portalleafs);
	memset (uncompressed, 0, bitbytes*portalleafs);


	CalcVis ();

	printf ("c_chains: %i\n",c_chains);

	visdatasize = vismap_p - dvisdata;
	printf ("visdatasize:%i  compressed from %i\n", visdatasize, originalvismapsize);

	CalcAmbientSounds ();

	WriteBSPFile (source);

//      unlink (portalfile);
	if (GilMode)
		PrintStats();
	end = I_FloatTime ();
	printf ("%5.1f seconds elapsed\n", end-start);

	return 0;
}
