Code Search for Developers
 
 
  

faces.c from Nxabega at Krugle


Show faces.c syntax highlighted

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// faces.c

#include "qbsp.h"
#include "l_mem.h"

/*

  some faces will be removed before saving, but still form nodes:

  the insides of sky volumes
  meeting planes of different water current volumes

*/

// undefine for dumb linear searches
#define	USE_HASHING

#define	INTEGRAL_EPSILON	0.01
#define	POINT_EPSILON		0.5
#define	OFF_EPSILON			0.5

int             c_merge;
int             c_subdivide;

int             c_totalverts;
int             c_uniqueverts;
int             c_degenerate;
int             c_tjunctions;
int             c_faceoverflows;
int             c_facecollapse;
int             c_badstartverts;

#define	MAX_SUPERVERTS	512
int             superverts[MAX_SUPERVERTS];
int             numsuperverts;

face_t         *edgefaces[MAX_MAP_EDGES][2];
int             firstmodeledge = 1;
int             firstmodelface;

int             c_tryedges;

vec3_t          edge_dir;
vec3_t          edge_start;
vec_t           edge_len;

int             num_edge_verts;
int             edge_verts[MAX_MAP_VERTS];

face_t         *NewFaceFromFace(face_t * f);

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

typedef struct hashvert_s
{
	struct hashvert_s *next;
	int             num;
} hashvert_t;


#define	HASH_SIZE	64


int             vertexchain[MAX_MAP_VERTS];	// the next vertex in a hash chain
int             hashverts[HASH_SIZE * HASH_SIZE];	// a vertex number, or 0 for no verts

face_t         *edgefaces[MAX_MAP_EDGES][2];

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


unsigned HashVec(vec3_t vec)
{
	int             x, y;

	x = (4096 + (int)(vec[0] + 0.5)) >> 7;
	y = (4096 + (int)(vec[1] + 0.5)) >> 7;

	if(x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE)
		Error("HashVec: point outside valid range");

	return y * HASH_SIZE + x;
}

#ifdef USE_HASHING
/*
=============
GetVertex

Uses hashing
=============
*/
int GetVertexnum(vec3_t in)
{
	int             h;
	int             i;
	float          *p;
	vec3_t          vert;
	int             vnum;

	c_totalverts++;

	for(i = 0; i < 3; i++)
	{
		if(fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)
			vert[i] = Q_rint(in[i]);
		else
			vert[i] = in[i];
	}

	h = HashVec(vert);

	for(vnum = hashverts[h]; vnum; vnum = vertexchain[vnum])
	{
		p = dvertexes[vnum].point;
		if(fabs(p[0] - vert[0]) < POINT_EPSILON && fabs(p[1] - vert[1]) < POINT_EPSILON && fabs(p[2] - vert[2]) < POINT_EPSILON)
			return vnum;
	}

// emit a vertex
	if(numvertexes == MAX_MAP_VERTS)
		Error("numvertexes == MAX_MAP_VERTS");

	dvertexes[numvertexes].point[0] = vert[0];
	dvertexes[numvertexes].point[1] = vert[1];
	dvertexes[numvertexes].point[2] = vert[2];

	vertexchain[numvertexes] = hashverts[h];
	hashverts[h] = numvertexes;

	c_uniqueverts++;

	numvertexes++;

	return numvertexes - 1;
}
#else
/*
==================
GetVertexnum

Dumb linear search
==================
*/
int GetVertexnum(vec3_t v)
{
	int             i, j;
	dvertex_t      *dv;
	vec_t           d;

	c_totalverts++;

	// make really close values exactly integral
	for(i = 0; i < 3; i++)
	{
		if(fabs(v[i] - (int)(v[i] + 0.5)) < INTEGRAL_EPSILON)
			v[i] = (int)(v[i] + 0.5);
		if(v[i] < -4096 || v[i] > 4096)
			Error("GetVertexnum: outside +/- 4096");
	}

	// search for an existing vertex match
	for(i = 0, dv = dvertexes; i < numvertexes; i++, dv++)
	{
		for(j = 0; j < 3; j++)
		{
			d = v[j] - dv->point[j];
			if(d > POINT_EPSILON || d < -POINT_EPSILON)
				break;
		}
		if(j == 3)
			return i;			// a match
	}

	// new point
	if(numvertexes == MAX_MAP_VERTS)
		Error("MAX_MAP_VERTS");
	VectorCopy(v, dv->point);
	numvertexes++;
	c_uniqueverts++;

	return numvertexes - 1;
}
#endif


/*
==================
FaceFromSuperverts

The faces vertexes have been added to the superverts[] array,
and there may be more there than can be held in a face (MAXEDGES).

If less, the faces vertexnums[] will be filled in, otherwise
face will reference a tree of split[] faces until all of the
vertexnums can be added.

superverts[base] will become face->vertexnums[0], and the others
will be circularly filled in.
==================
*/
void FaceFromSuperverts(node_t * node, face_t * f, int base)
{
	face_t         *newf;
	int             remaining;
	int             i;

	remaining = numsuperverts;
	while(remaining > MAXEDGES)
	{							// must split into two faces, because of vertex overload
		c_faceoverflows++;

		newf = f->split[0] = NewFaceFromFace(f);
		newf = f->split[0];
		newf->next = node->faces;
		node->faces = newf;

		newf->numpoints = MAXEDGES;
		for(i = 0; i < MAXEDGES; i++)
			newf->vertexnums[i] = superverts[(i + base) % numsuperverts];

		f->split[1] = NewFaceFromFace(f);
		f = f->split[1];
		f->next = node->faces;
		node->faces = f;

		remaining -= (MAXEDGES - 2);
		base = (base + MAXEDGES - 1) % numsuperverts;
	}

	// copy the vertexes back to the face
	f->numpoints = remaining;
	for(i = 0; i < remaining; i++)
		f->vertexnums[i] = superverts[(i + base) % numsuperverts];
}


/*
==================
EmitFaceVertexes
==================
*/
void EmitFaceVertexes(node_t * node, face_t * f)
{
	winding_t      *w;
	int             i;

	if(f->merged || f->split[0] || f->split[1])
		return;

	w = f->w;
	for(i = 0; i < w->numpoints; i++)
	{
		if(noweld)
		{						// make every point unique
			if(numvertexes == MAX_MAP_VERTS)
				Error("MAX_MAP_VERTS");
			superverts[i] = numvertexes;
			VectorCopy(w->p[i], dvertexes[numvertexes].point);
			numvertexes++;
			c_uniqueverts++;
			c_totalverts++;
		}
		else
			superverts[i] = GetVertexnum(w->p[i]);
	}
	numsuperverts = w->numpoints;

	// this may fragment the face if > MAXEDGES
	FaceFromSuperverts(node, f, 0);
}

/*
==================
EmitVertexes_r
==================
*/
void EmitVertexes_r(node_t * node)
{
	int             i;
	face_t         *f;

	if(node->planenum == PLANENUM_LEAF)
		return;

	for(f = node->faces; f; f = f->next)
	{
		EmitFaceVertexes(node, f);
	}

	for(i = 0; i < 2; i++)
		EmitVertexes_r(node->children[i]);
}


#ifdef USE_HASHING
/*
==========
FindEdgeVerts

Uses the hash tables to cut down to a small number
==========
*/
void FindEdgeVerts(vec3_t v1, vec3_t v2)
{
	int             x1, x2, y1, y2, t;
	int             x, y;
	int             vnum;

#if 0
	{
		int             i;

		num_edge_verts = numvertexes - 1;
		for(i = 0; i < numvertexes - 1; i++)
			edge_verts[i] = i + 1;
	}
#endif

	x1 = (4096 + (int)(v1[0] + 0.5)) >> 7;
	y1 = (4096 + (int)(v1[1] + 0.5)) >> 7;
	x2 = (4096 + (int)(v2[0] + 0.5)) >> 7;
	y2 = (4096 + (int)(v2[1] + 0.5)) >> 7;

	if(x1 > x2)
	{
		t = x1;
		x1 = x2;
		x2 = t;
	}
	if(y1 > y2)
	{
		t = y1;
		y1 = y2;
		y2 = t;
	}
#if 0
	x1--;
	x2++;
	y1--;
	y2++;
	if(x1 < 0)
		x1 = 0;
	if(x2 >= HASH_SIZE)
		x2 = HASH_SIZE;
	if(y1 < 0)
		y1 = 0;
	if(y2 >= HASH_SIZE)
		y2 = HASH_SIZE;
#endif
	num_edge_verts = 0;
	for(x = x1; x <= x2; x++)
	{
		for(y = y1; y <= y2; y++)
		{
			for(vnum = hashverts[y * HASH_SIZE + x]; vnum; vnum = vertexchain[vnum])
			{
				edge_verts[num_edge_verts++] = vnum;
			}
		}
	}
}

#else
/*
==========
FindEdgeVerts

Forced a dumb check of everything
==========
*/
void FindEdgeVerts(vec3_t v1, vec3_t v2)
{
	int             i;

	num_edge_verts = numvertexes - 1;
	for(i = 0; i < num_edge_verts; i++)
		edge_verts[i] = i + 1;
}
#endif

/*
==========
TestEdge

Can be recursively reentered
==========
*/
void TestEdge(vec_t start, vec_t end, int p1, int p2, int startvert)
{
	int             j, k;
	vec_t           dist;
	vec3_t          delta;
	vec3_t          exact;
	vec3_t          off;
	vec_t           error;
	vec3_t          p;

	if(p1 == p2)
	{
		c_degenerate++;
		return;					// degenerate edge
	}

	for(k = startvert; k < num_edge_verts; k++)
	{
		j = edge_verts[k];
		if(j == p1 || j == p2)
			continue;

		VectorCopy(dvertexes[j].point, p);

		VectorSubtract(p, edge_start, delta);
		dist = DotProduct(delta, edge_dir);
		if(dist <= start || dist >= end)
			continue;			// off an end
		VectorMA(edge_start, dist, edge_dir, exact);
		VectorSubtract(p, exact, off);
		error = VectorLength(off);

		if(fabs(error) > OFF_EPSILON)
			continue;			// not on the edge

		// break the edge
		c_tjunctions++;
		TestEdge(start, dist, p1, j, k + 1);
		TestEdge(dist, end, j, p2, k + 1);
		return;
	}

	// the edge p1 to p2 is now free of tjunctions
	if(numsuperverts >= MAX_SUPERVERTS)
		Error("MAX_SUPERVERTS");
	superverts[numsuperverts] = p1;
	numsuperverts++;
}

/*
==================
FixFaceEdges

==================
*/
void FixFaceEdges(node_t * node, face_t * f)
{
	int             p1, p2;
	int             i;
	vec3_t          e2;
	vec_t           len;
	int             count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
	int             base;

	if(f->merged || f->split[0] || f->split[1])
		return;

	numsuperverts = 0;

	for(i = 0; i < f->numpoints; i++)
	{
		p1 = f->vertexnums[i];
		p2 = f->vertexnums[(i + 1) % f->numpoints];

		VectorCopy(dvertexes[p1].point, edge_start);
		VectorCopy(dvertexes[p2].point, e2);

		FindEdgeVerts(edge_start, e2);

		VectorSubtract(e2, edge_start, edge_dir);
		len = VectorNormalize(edge_dir);

		start[i] = numsuperverts;
		TestEdge(0, len, p1, p2, 0);

		count[i] = numsuperverts - start[i];
	}

	if(numsuperverts < 3)
	{							// entire face collapsed
		f->numpoints = 0;
		c_facecollapse++;
		return;
	}

	// we want to pick a vertex that doesn't have tjunctions
	// on either side, which can cause artifacts on trifans,
	// especially underwater
	for(i = 0; i < f->numpoints; i++)
	{
		if(count[i] == 1 && count[(i + f->numpoints - 1) % f->numpoints] == 1)
			break;
	}
	if(i == f->numpoints)
	{
		f->badstartvert = true;
		c_badstartverts++;
		base = 0;
	}
	else
	{							// rotate the vertex order
		base = start[i];
	}

	// this may fragment the face if > MAXEDGES
	FaceFromSuperverts(node, f, base);
}

/*
==================
FixEdges_r
==================
*/
void FixEdges_r(node_t * node)
{
	int             i;
	face_t         *f;

	if(node->planenum == PLANENUM_LEAF)
		return;

	for(f = node->faces; f; f = f->next)
		FixFaceEdges(node, f);

	for(i = 0; i < 2; i++)
		FixEdges_r(node->children[i]);
}

/*
===========
FixTjuncs

===========
*/
void FixTjuncs(node_t * headnode)
{
	// snap and merge all vertexes
	qprintf("---- snap verts ----\n");
	memset(hashverts, 0, sizeof(hashverts));
	c_totalverts = 0;
	c_uniqueverts = 0;
	c_faceoverflows = 0;
	EmitVertexes_r(headnode);
	qprintf("%i unique from %i\n", c_uniqueverts, c_totalverts);

	// break edges on tjunctions
	qprintf("---- tjunc ----\n");
	c_tryedges = 0;
	c_degenerate = 0;
	c_facecollapse = 0;
	c_tjunctions = 0;
	if(!notjunc)
		FixEdges_r(headnode);
	qprintf("%5i edges degenerated\n", c_degenerate);
	qprintf("%5i faces degenerated\n", c_facecollapse);
	qprintf("%5i edges added by tjunctions\n", c_tjunctions);
	qprintf("%5i faces added by tjunctions\n", c_faceoverflows);
	qprintf("%5i bad start verts\n", c_badstartverts);
}


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

int             c_faces;

face_t         *AllocFace(void)
{
	face_t         *f;

	f = GetMemory(sizeof(*f));
	memset(f, 0, sizeof(*f));
	c_faces++;

	return f;
}

face_t         *NewFaceFromFace(face_t * f)
{
	face_t         *newf;

	newf = AllocFace();
	*newf = *f;
	newf->merged = NULL;
	newf->split[0] = newf->split[1] = NULL;
	newf->w = NULL;
	return newf;
}

void FreeFace(face_t * f)
{
	if(f->w)
		FreeWinding(f->w);
	FreeMemory(f);
	c_faces--;
}

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

/*
==================
GetEdge

Called by writebsp.
Don't allow four way edges
==================
*/
int GetEdge2(int v1, int v2, face_t * f)
{
	dedge_t        *edge;
	int             i;

	c_tryedges++;

	if(!noshare)
	{
		for(i = firstmodeledge; i < numedges; i++)
		{
			edge = &dedges[i];
			if(v1 == edge->v[1] && v2 == edge->v[0] && edgefaces[i][0]->contents == f->contents)
			{
				if(edgefaces[i][1])
					//              printf ("WARNING: multiple backward edge\n");
					continue;
				edgefaces[i][1] = f;
				return -i;
			}
#if 0
			if(v1 == edge->v[0] && v2 == edge->v[1])
			{
				printf("WARNING: multiple forward edge\n");
				return i;
			}
#endif
		}
	}

// emit an edge
	if(numedges >= MAX_MAP_EDGES)
		Error("numedges == MAX_MAP_EDGES");
	edge = &dedges[numedges];
	numedges++;
	edge->v[0] = v1;
	edge->v[1] = v2;
	edgefaces[numedges - 1][0] = f;

	return numedges - 1;
}

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

FACE MERGING

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

/*
=============
TryMerge

If two polygons share a common edge and the edges that meet at the
common points are both inside the other polygons, merge them

Returns NULL if the faces couldn't be merged, or the new face.
The originals will NOT be freed.
=============
*/
face_t         *TryMerge(face_t * f1, face_t * f2, vec3_t planenormal)
{
	face_t         *newf;
	winding_t      *nw;

	if(!f1->w || !f2->w)
		return NULL;
	if(f1->texinfo != f2->texinfo)
		return NULL;
	if(f1->planenum != f2->planenum)	// on front and back sides
		return NULL;
	if(f1->contents != f2->contents)
		return NULL;


	nw = TryMergeWinding(f1->w, f2->w, planenormal);
	if(!nw)
		return NULL;

	c_merge++;
	newf = NewFaceFromFace(f1);
	newf->w = nw;

	f1->merged = newf;
	f2->merged = newf;

	return newf;
}

/*
===============
MergeNodeFaces
===============
*/
void MergeNodeFaces(node_t * node)
{
	face_t         *f1, *f2, *end;
	face_t         *merged;
	plane_t        *plane;

	plane = &mapplanes[node->planenum];
	merged = NULL;

	for(f1 = node->faces; f1; f1 = f1->next)
	{
		if(f1->merged || f1->split[0] || f1->split[1])
			continue;

		for(f2 = node->faces; f2 != f1; f2 = f2->next)
		{
			if(f2->merged || f2->split[0] || f2->split[1])
				continue;

			//IDBUG: always passes the face's node's normal to TryMerge()
			//regardless of which side the face is on. Approximately 50% of
			//the time the face will be on the other side of node, and thus
			//the result of the convex/concave test in TryMergeWinding(),
			//which depends on the normal, is flipped. This causes faces
			//that shouldn't be merged to be merged and faces that
			//should be merged to not be merged. 
			//the following added line fixes this bug
			//thanks to: Alexander Malmberg <alexander@malmberg.org>
			plane = &mapplanes[f1->planenum];
			//
			merged = TryMerge(f1, f2, plane->normal);
			if(!merged)
				continue;

			// add merged to the end of the node face list 
			// so it will be checked against all the faces again
			for(end = node->faces; end->next; end = end->next)
				;
			merged->next = NULL;
			end->next = merged;
			break;
		}
	}
}

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

/*
===============
SubdivideFace

Chop up faces that are larger than we want in the surface cache
===============
*/
void SubdivideFace(node_t * node, face_t * f)
{
	float           mins, maxs;
	vec_t           v;
	int             axis, i;
	texinfo_t      *tex;
	vec3_t          temp;
	vec_t           dist;
	winding_t      *w, *frontw, *backw;

	if(f->merged)
		return;

// special (non-surface cached) faces don't need subdivision
	tex = &texinfo[f->texinfo];

	if(tex->flags & (SURF_WARP | SURF_SKY))
	{
		return;
	}

	for(axis = 0; axis < 2; axis++)
	{
		while(1)
		{
			mins = 999999;
			maxs = -999999;

			VectorCopy(tex->vecs[axis], temp);
			w = f->w;
			for(i = 0; i < w->numpoints; i++)
			{
				v = DotProduct(w->p[i], temp);
				if(v < mins)
					mins = v;
				if(v > maxs)
					maxs = v;
			}
#if 0
			if(maxs - mins <= 0)
				Error("zero extents");
#endif
			if(axis == 2)
			{					// allow double high walls
				if(maxs - mins <= subdivide_size /* *2 */ )
					break;
			}
			else if(maxs - mins <= subdivide_size)
				break;

			// split it
			c_subdivide++;

			v = VectorNormalize(temp);

			dist = (mins + subdivide_size - 16) / v;

			ClipWindingEpsilon(w, temp, dist, ON_EPSILON, &frontw, &backw);
			if(!frontw || !backw)
				Error("SubdivideFace: didn't split the polygon");

			f->split[0] = NewFaceFromFace(f);
			f->split[0]->w = frontw;
			f->split[0]->next = node->faces;
			node->faces = f->split[0];

			f->split[1] = NewFaceFromFace(f);
			f->split[1]->w = backw;
			f->split[1]->next = node->faces;
			node->faces = f->split[1];

			SubdivideFace(node, f->split[0]);
			SubdivideFace(node, f->split[1]);
			return;
		}
	}
}

void SubdivideNodeFaces(node_t * node)
{
	face_t         *f;

	for(f = node->faces; f; f = f->next)
	{
		SubdivideFace(node, f);
	}
}

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

int             c_nodefaces;


/*
============
FaceFromPortal

============
*/
face_t         *FaceFromPortal(portal_t * p, int pside)
{
	face_t         *f;
	side_t         *side;

	side = p->side;
	if(!side)
		return NULL;			// portal does not bridge different visible contents

	f = AllocFace();

	f->texinfo = side->texinfo;
	f->planenum = (side->planenum & ~1) | pside;
	f->portal = p;

	if((p->nodes[pside]->contents & CONTENTS_WINDOW)
	   && VisibleContents(p->nodes[!pside]->contents ^ p->nodes[pside]->contents) == CONTENTS_WINDOW)
		return NULL;			// don't show insides of windows

	if(pside)
	{
		f->w = ReverseWinding(p->winding);
		f->contents = p->nodes[1]->contents;
	}
	else
	{
		f->w = CopyWinding(p->winding);
		f->contents = p->nodes[0]->contents;
	}
	return f;
}


/*
===============
MakeFaces_r

If a portal will make a visible face,
mark the side that originally created it

  solid / empty : solid
  solid / water : solid
  water / empty : water
  water / water : none
===============
*/
void MakeFaces_r(node_t * node)
{
	portal_t       *p;
	int             s;

	// recurse down to leafs
	if(node->planenum != PLANENUM_LEAF)
	{
		MakeFaces_r(node->children[0]);
		MakeFaces_r(node->children[1]);

		// merge together all visible faces on the node
		if(!nomerge)
			MergeNodeFaces(node);
		if(!nosubdiv)
			SubdivideNodeFaces(node);

		return;
	}

	// solid leafs never have visible faces
	if(node->contents & CONTENTS_SOLID)
		return;

	// see which portals are valid
	for(p = node->portals; p; p = p->next[s])
	{
		s = (p->nodes[1] == node);

		p->face[s] = FaceFromPortal(p, s);
		if(p->face[s])
		{
			c_nodefaces++;
			p->face[s]->next = p->onnode->faces;
			p->onnode->faces = p->face[s];
		}
	}
}

/*
============
MakeFaces
============
*/
void MakeFaces(node_t * node)
{
	qprintf("--- MakeFaces ---\n");
	c_merge = 0;
	c_subdivide = 0;
	c_nodefaces = 0;

	MakeFaces_r(node);

	qprintf("%5i makefaces\n", c_nodefaces);
	qprintf("%5i merged\n", c_merge);
	qprintf("%5i subdivided\n", c_subdivide);
}




See more files for this project here

Nxabega

Nxabega is a First Person Shooter (FPS) based upon the open sourced Quake 3 engine and game code. The final intention is to provide a rich single player game.

Project homepage: http://sourceforge.net/projects/nxabega
Programming language(s): C,C++
License: other

  Conscript
  Makefile
  _files.c
  aas_areamerging.c
  aas_areamerging.h
  aas_cfg.c
  aas_cfg.h
  aas_create.c
  aas_create.h
  aas_edgemelting.c
  aas_edgemelting.h
  aas_facemerging.c
  aas_facemerging.h
  aas_file.c
  aas_file.h
  aas_gsubdiv.c
  aas_gsubdiv.h
  aas_map.c
  aas_map.h
  aas_prunenodes.c
  aas_prunenodes.h
  aas_store.c
  aas_store.h
  aasfile.h
  be_aas_bspc.c
  be_aas_bspc.h
  brushbsp.c
  bspc.c
  bspc.sln
  bspc.vcproj
  cfgq3.c
  csg.c
  faces.c
  gldraw.c
  glfile.c
  l_bsp_ent.c
  l_bsp_ent.h
  l_bsp_hl.c
  l_bsp_hl.h
  l_bsp_q1.c
  l_bsp_q1.h
  l_bsp_q2.c
  l_bsp_q2.h
  l_bsp_q3.c
  l_bsp_q3.h
  l_bsp_sin.c
  l_bsp_sin.h
  l_cmd.c
  l_cmd.h
  l_log.c
  l_log.h
  l_math.c
  l_math.h
  l_mem.c
  l_mem.h
  l_poly.c
  l_poly.h
  l_qfiles.c
  l_qfiles.h
  l_threads.c
  l_threads.h
  l_utils.c
  l_utils.h
  lcc.mak
  leakfile.c
  linux-i386.mak
  map.c
  map_hl.c
  map_q1.c
  map_q2.c
  map_q3.c
  map_sin.c
  nodraw.c
  portals.c
  prtfile.c
  q2files.h
  q3files.h
  qbsp.h
  qfiles.h
  sinfiles.h
  tetrahedron.c
  tetrahedron.h
  textures.c
  tree.c
  writebsp.c