diff options
Diffstat (limited to 'code/bspc')
85 files changed, 39102 insertions, 0 deletions
diff --git a/code/bspc/Conscript b/code/bspc/Conscript new file mode 100755 index 0000000..08487ab --- /dev/null +++ b/code/bspc/Conscript @@ -0,0 +1,75 @@ +# bspc compile
 +
 +Import qw( BSPC_BASE_CFLAGS BUILD_DIR INSTALL_DIR CC CXX LINK );
 +
 +@BSPC_FILES = qw(
 +	aas_areamerging.c
 +	aas_cfg.c
 +	aas_create.c
 +	aas_edgemelting.c
 +	aas_facemerging.c
 +	aas_file.c
 +	aas_gsubdiv.c
 +	aas_map.c
 +	aas_prunenodes.c
 +	aas_store.c
 +	be_aas_bspc.c
 +	../botlib/be_aas_bspq3.c
 +	../botlib/be_aas_cluster.c
 +	../botlib/be_aas_move.c
 +	../botlib/be_aas_optimize.c
 +	../botlib/be_aas_reach.c
 +	../botlib/be_aas_sample.c
 +	brushbsp.c
 +	bspc.c
 +	../qcommon/cm_load.c
 +	../qcommon/cm_patch.c
 +	../qcommon/cm_test.c
 +	../qcommon/cm_trace.c
 +	csg.c
 +	glfile.c
 +	l_bsp_ent.c
 +	l_bsp_hl.c
 +	l_bsp_q1.c
 +	l_bsp_q2.c
 +	l_bsp_q3.c
 +	l_bsp_sin.c
 +	l_cmd.c
 +	../botlib/l_libvar.c
 +	l_log.c
 +	l_math.c
 +	l_mem.c
 +	l_poly.c
 +	../botlib/l_precomp.c
 +	l_qfiles.c
 +	../botlib/l_script.c
 +	../botlib/l_struct.c
 +	l_threads.c
 +	l_utils.c
 +	leakfile.c
 +	map.c
 +	map_hl.c
 +	map_q1.c
 +	map_q2.c
 +	map_q3.c
 +	map_sin.c
 +	../qcommon/md4.c
 +	nodraw.c
 +	portals.c
 +	textures.c
 +	tree.c
 +	../qcommon/unzip.c
 +  );
 +$BSPC_REF = \@BSPC_FILES;
 +
 +$env = new cons(
 +  CC => $CC,
 +  CXX => $CXX,
 +  LINK => $LINK,
 +  CFLAGS => $BSPC_BASE_CFLAGS,
 +  LIBS => '-ldl -lm -lpthread'
 +);
 +
 +Program $env 'bspc', @$BSPC_REF;
 +# this should install to Q3 or something?
 +Install $env $INSTALL_DIR, 'bspc';
 diff --git a/code/bspc/Makefile b/code/bspc/Makefile new file mode 100755 index 0000000..2f16169 --- /dev/null +++ b/code/bspc/Makefile @@ -0,0 +1,114 @@ +#
 +# Makefile for the BSPC tool for the Gladiator Bot
 +# Intended for gcc/Linux
 +#
 +# TTimo 5/15/2001
 +# some cleanup .. only used on i386 for GtkRadiant setups AFAIK .. removing the i386 tag
 +# TODO: the intermediate object files should go into their own directory
 +#   specially for ../botlib and ../qcommon, the compilation flags on those might not be what you expect
 +
 +#ARCH=i386
 +CC=gcc
 +BASE_CFLAGS=-Dstricmp=strcasecmp
 +
 +#use these cflags to optimize it
 +CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \
 +	-fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \
 +	-malign-jumps=2 -malign-functions=2 -DLINUX -DBSPC
 +#use these when debugging 
 +#CFLAGS=$(BASE_CFLAGS) -g
 +
 +LDFLAGS=-ldl -lm -lpthread
 +
 +DO_CC=$(CC) $(CFLAGS) -o $@ -c $<
 +
 +#############################################################################
 +# SETUP AND BUILD BSPC
 +#############################################################################
 +
 +.c.o:
 +	$(DO_CC)
 +
 +GAME_OBJS = \
 +	_files.o\
 +	aas_areamerging.o\
 +	aas_cfg.o\
 +	aas_create.o\
 +	aas_edgemelting.o\
 +	aas_facemerging.o\
 +	aas_file.o\
 +	aas_gsubdiv.o\
 +	aas_map.o\
 +	aas_prunenodes.o\
 +	aas_store.o\
 +	be_aas_bspc.o\
 +	../botlib/be_aas_bspq3.o\
 +	../botlib/be_aas_cluster.o\
 +	../botlib/be_aas_move.o\
 +	../botlib/be_aas_optimize.o\
 +	../botlib/be_aas_reach.o\
 +	../botlib/be_aas_sample.o\
 +	brushbsp.o\
 +	bspc.o\
 +	../qcommon/cm_load.o\
 +	../qcommon/cm_patch.o\
 +	../qcommon/cm_test.o\
 +	../qcommon/cm_trace.o\
 +	csg.o\
 +	glfile.o\
 +	l_bsp_ent.o\
 +	l_bsp_hl.o\
 +	l_bsp_q1.o\
 +	l_bsp_q2.o\
 +	l_bsp_q3.o\
 +	l_bsp_sin.o\
 +	l_cmd.o\
 +	../botlib/l_libvar.o\
 +	l_log.o\
 +	l_math.o\
 +	l_mem.o\
 +	l_poly.o\
 +	../botlib/l_precomp.o\
 +	l_qfiles.o\
 +	../botlib/l_script.o\
 +	../botlib/l_struct.o\
 +	l_threads.o\
 +	l_utils.o\
 +	leakfile.o\
 +	map.o\
 +	map_hl.o\
 +	map_q1.o\
 +	map_q2.o\
 +	map_q3.o\
 +	map_sin.o\
 +	../qcommon/md4.o\
 +	nodraw.o\
 +	portals.o\
 +	textures.o\
 +	tree.o\
 +	../qcommon/unzip.o
 +
 +        #tetrahedron.o
 +
 +bspc : $(GAME_OBJS)
 +	$(CC) $(CFLAGS) -o $@ $(GAME_OBJS) $(LDFLAGS)
 +	strip $@
 +
 +
 +#############################################################################
 +# MISC
 +#############################################################################
 +
 +clean:
 +	-rm -f $(GAME_OBJS)
 +
 +depend:
 +	gcc -MM $(GAME_OBJS:.o=.c)
 +
 +#install:
 +#	cp bspci386 ..
 +
 +#
 +# From "make depend"
 +#
 +
 diff --git a/code/bspc/_files.c b/code/bspc/_files.c new file mode 100755 index 0000000..8f4fcff --- /dev/null +++ b/code/bspc/_files.c @@ -0,0 +1,63 @@ +//=========================================================================== +// +// Name:			_files.c +// Function: +// Programmer:		Mr Elusive +// Last update:		1999-12-02 +// Tab Size:		4 +//=========================================================================== + +/* + +aas_areamerging.c			//AAS area merging +aas_cfg.c					//AAS configuration for different games +aas_create.c				//AAS creating +aas_edgemelting.c			//AAS edge melting +aas_facemerging.c			//AAS face merging +aas_file.c					//AAS file writing +aas_gsubdiv.c				//AAS gravitational and ladder subdivision +aas_map.c					//AAS map brush creation +aas_prunenodes.c			//AAS node pruning +aas_store.c					//AAS file storing + +map.c						//map file loading and writing +map_hl.c					//Half-Life map loading +map_q1.c					//Quake1 map loading +map_q2.c					//Quake2 map loading +map_q3.c					//Quake3 map loading +map_sin.c					//Sin map loading +tree.c						//BSP tree management + node pruning			(*) +brushbsp.c					//brush bsp creation							(*) +portals.c					//BSP portal creation and leaf filling			(*) +csg.c						//Constructive Solid Geometry brush chopping	(*) +leakfile.c					//leak file writing								(*) +textures.c					//Quake2 BSP textures							(*) + +l_bsp_ent.c					//BSP entity parsing +l_bsp_hl.c					//Half-Life BSP loading and writing +l_bsp_q1.c					//Quake1 BSP loading and writing +l_bsp_q2.c					//Quake2 BSP loading and writing +l_bsp_q3.c					//Quake2 BSP loading and writing +l_bsp_sin.c					//Sin BSP loading and writing +l_cmd.c						//cmd library +l_log.c						//log file library +l_math.c					//math library +l_mem.c						//memory management library +l_poly.c					//polygon (winding) library +l_script.c					//script file parsing library +l_threads.c					//multi-threading library +l_utils.c					//utility library +l_qfiles.c					//loading of quake files + +gldraw.c					//GL drawing									(*) +glfile.c					//GL file writing								(*) +nodraw.c					//no draw module								(*) + +bspc.c						//BSPC Win32 console version +winbspc.c					//WinBSPC Win32 GUI version +win32_terminal.c			//Win32 terminal output +win32_qfiles.c				//Win32 game file management (also .pak .sin) +win32_font.c				//Win32 fonts +win32_folder.c				//Win32 folder dialogs + +*/ diff --git a/code/bspc/aas_areamerging.c b/code/bspc/aas_areamerging.c new file mode 100755 index 0000000..c5f82d2 --- /dev/null +++ b/code/bspc/aas_areamerging.c @@ -0,0 +1,390 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_create.h"
 +#include "aas_store.h"
 +
 +#define CONVEX_EPSILON		0.3
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_RefreshMergedTree_r(tmp_node_t *tmpnode)
 +{
 +	tmp_area_t *tmparea;
 +
 +	//if this is a solid leaf
 +	if (!tmpnode) return NULL;
 +	//if this is an area leaf
 +	if (tmpnode->tmparea)
 +	{
 +		tmparea = tmpnode->tmparea;
 +		while(tmparea->mergedarea) tmparea = tmparea->mergedarea;
 +		tmpnode->tmparea = tmparea;
 +		return tmpnode;
 +	} //end if
 +	//do the children recursively
 +	tmpnode->children[0] = AAS_RefreshMergedTree_r(tmpnode->children[0]);
 +	tmpnode->children[1] = AAS_RefreshMergedTree_r(tmpnode->children[1]);
 +	return tmpnode;
 +} //end of the function AAS_RefreshMergedTree_r
 +//===========================================================================
 +// returns true if the two given faces would create a non-convex area at
 +// the given sides, otherwise false is returned
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int NonConvex(tmp_face_t *face1, tmp_face_t *face2, int side1, int side2)
 +{
 +	int i;
 +	winding_t *w1, *w2;
 +	plane_t *plane1, *plane2;
 +	
 +	w1 = face1->winding;
 +	w2 = face2->winding;
 +
 +	plane1 = &mapplanes[face1->planenum ^ side1];
 +	plane2 = &mapplanes[face2->planenum ^ side2];
 +
 +	//check if one of the points of face1 is at the back of the plane of face2
 +	for (i = 0; i < w1->numpoints; i++)
 +	{
 +		if (DotProduct(plane2->normal, w1->p[i]) - plane2->dist < -CONVEX_EPSILON) return true;
 +	} //end for
 +	//check if one of the points of face2 is at the back of the plane of face1
 +	for (i = 0; i < w2->numpoints; i++)
 +	{
 +		if (DotProduct(plane1->normal, w2->p[i]) - plane1->dist < -CONVEX_EPSILON) return true;
 +	} //end for
 +
 +	return false;
 +} //end of the function NonConvex
 +//===========================================================================
 +// try to merge the areas at both sides of the given face
 +//
 +// Parameter:				seperatingface		: face that seperates two areas
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_TryMergeFaceAreas(tmp_face_t *seperatingface)
 +{
 +	int side1, side2, area1faceflags, area2faceflags;
 +	tmp_area_t *tmparea1, *tmparea2, *newarea;
 +	tmp_face_t *face1, *face2, *nextface1, *nextface2;
 +
 +	tmparea1 = seperatingface->frontarea;
 +	tmparea2 = seperatingface->backarea;
 +
 +	//areas must have the same presence type
 +	if (tmparea1->presencetype != tmparea2->presencetype) return false;
 +	//areas must have the same area contents
 +	if (tmparea1->contents != tmparea2->contents) return false;
 +	//areas must have the same bsp model inside (or both none)
 +	if (tmparea1->modelnum != tmparea2->modelnum) return false;
 +
 +	area1faceflags = 0;
 +	area2faceflags = 0;
 +	for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		side1 = (face1->frontarea != tmparea1);
 +		//debug: check if the area belongs to the area
 +		if (face1->frontarea != tmparea1 &&
 +				face1->backarea != tmparea1) Error("face does not belong to area1");
 +		//just continue if the face is seperating the two areas
 +		//NOTE: a result of this is that ground and gap areas can
 +		//      be merged if the seperating face is the gap
 +		if ((face1->frontarea == tmparea1 &&
 +				face1->backarea == tmparea2) ||
 +				(face1->frontarea == tmparea2 &&
 +				face1->backarea == tmparea1)) continue;
 +		//get area1 face flags
 +		area1faceflags |= face1->faceflags;
 +		if (AAS_GapFace(face1, side1)) area1faceflags |= FACE_GAP;
 +		//
 +		for (face2 = tmparea2->tmpfaces; face2; face2 = face2->next[side2])
 +		{
 +			side2 = (face2->frontarea != tmparea2);
 +			//debug: check if the area belongs to the area
 +			if (face2->frontarea != tmparea2 &&
 +					face2->backarea != tmparea2) Error("face does not belong to area2");
 +			//just continue if the face is seperating the two areas
 +			//NOTE: a result of this is that ground and gap areas can
 +			//      be merged if the seperating face is the gap
 +			if ((face2->frontarea == tmparea1 &&
 +					face2->backarea == tmparea2) ||
 +					(face2->frontarea == tmparea2 &&
 +					face2->backarea == tmparea1)) continue;
 +			//get area2 face flags
 +			area2faceflags |= face2->faceflags;
 +			if (AAS_GapFace(face2, side2)) area2faceflags |= FACE_GAP;
 +			//if the two faces would create a non-convex area
 +			if (NonConvex(face1, face2, side1, side2)) return false;
 +		} //end for
 +	} //end for
 +	//if one area has gap faces (that aren't seperating the two areas)
 +	//and the other has ground faces (that aren't seperating the two areas),
 +	//the areas can't be merged
 +	if (((area1faceflags & FACE_GROUND) && (area2faceflags & FACE_GAP)) ||
 +			((area2faceflags & FACE_GROUND) && (area1faceflags & FACE_GAP)))
 +	{
 +//		Log_Print("   can't merge: ground/gap\n");
 +		return false;
 +	} //end if
 +
 +//	Log_Print("merged area %d & %d to %d with %d faces\n", tmparea1->areanum, tmparea2->areanum, newarea->areanum, numfaces);
 +//	return false;
 +	//
 +	//AAS_CheckArea(tmparea1);
 +	//AAS_CheckArea(tmparea2);
 +	//create the new area
 +	newarea = AAS_AllocTmpArea();
 +	newarea->presencetype = tmparea1->presencetype;
 +	newarea->contents = tmparea1->contents;
 +	newarea->modelnum = tmparea1->modelnum;
 +	newarea->tmpfaces = NULL;
 +
 +	//add all the faces (except the seperating ones) from the first area
 +	//to the new area
 +	for (face1 = tmparea1->tmpfaces; face1; face1 = nextface1)
 +	{
 +		side1 = (face1->frontarea != tmparea1);
 +		nextface1 = face1->next[side1];
 +		//don't add seperating faces
 +		if ((face1->frontarea == tmparea1 &&
 +				face1->backarea == tmparea2) ||
 +				(face1->frontarea == tmparea2 &&
 +				face1->backarea == tmparea1))
 +		{
 +			continue;
 +		} //end if
 +		//
 +		AAS_RemoveFaceFromArea(face1, tmparea1);
 +		AAS_AddFaceSideToArea(face1, side1, newarea);
 +	} //end for
 +	//add all the faces (except the seperating ones) from the second area
 +	//to the new area
 +	for (face2 = tmparea2->tmpfaces; face2; face2 = nextface2)
 +	{
 +		side2 = (face2->frontarea != tmparea2);
 +		nextface2 = face2->next[side2];
 +		//don't add seperating faces
 +		if ((face2->frontarea == tmparea1 &&
 +				face2->backarea == tmparea2) ||
 +				(face2->frontarea == tmparea2 &&
 +				face2->backarea == tmparea1))
 +		{
 +			continue;
 +		} //end if
 +		//
 +		AAS_RemoveFaceFromArea(face2, tmparea2);
 +		AAS_AddFaceSideToArea(face2, side2, newarea);
 +	} //end for
 +	//free all shared faces
 +	for (face1 = tmparea1->tmpfaces; face1; face1 = nextface1)
 +	{
 +		side1 = (face1->frontarea != tmparea1);
 +		nextface1 = face1->next[side1];
 +		//
 +		AAS_RemoveFaceFromArea(face1, face1->frontarea);
 +		AAS_RemoveFaceFromArea(face1, face1->backarea);
 +		AAS_FreeTmpFace(face1);
 +	} //end for
 +	//
 +	tmparea1->mergedarea = newarea;
 +	tmparea1->invalid = true;
 +	tmparea2->mergedarea = newarea;
 +	tmparea2->invalid = true;
 +	//
 +	AAS_CheckArea(newarea);
 +	AAS_FlipAreaFaces(newarea);
 +//	Log_Print("merged area %d & %d to %d with %d faces\n", tmparea1->areanum, tmparea2->areanum, newarea->areanum);
 +	return true;
 +} //end of the function AAS_TryMergeFaceAreas
 +//===========================================================================
 +// try to merge areas
 +// merged areas are added to the end of the convex area list so merging
 +// will be tried for those areas as well
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		tmpaasworld
 +//===========================================================================
 +/*
 +void AAS_MergeAreas(void)
 +{
 +	int side, nummerges;
 +	tmp_area_t *tmparea, *othertmparea;
 +	tmp_face_t *face;
 +
 +	nummerges = 0;
 +	Log_Write("AAS_MergeAreas\r\n");
 +	qprintf("%6d areas merged", 1);
 +	//first merge grounded areas only
 +	//NOTE: this is useless because the area settings aren't available yet
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +//		Log_Print("checking area %d\n", i);
 +		//if the area is invalid
 +		if (tmparea->invalid)
 +		{
 +//			Log_Print("   area invalid\n");
 +			continue;
 +		} //end if
 +		//
 +//		if (!(tmparea->settings->areaflags & AREA_GROUNDED)) continue;
 +		//
 +		for (face = tmparea->tmpfaces; face; face = face->next[side])
 +		{
 +			side = (face->frontarea != tmparea);
 +			//if the face has both a front and back area
 +			if (face->frontarea && face->backarea)
 +			{
 +				//
 +				if (face->frontarea == tmparea) othertmparea = face->backarea;
 +				else othertmparea = face->frontarea;
 +//				if (!(othertmparea->settings->areaflags & AREA_GROUNDED)) continue;
 +//				Log_Print("  checking area %d with %d\n", face->frontarea, face->backarea);
 +				if (AAS_TryMergeFaceAreas(face))
 +				{
 +					qprintf("\r%6d", ++nummerges);
 +					break;
 +				} //end if
 +			} //end if
 +		} //end for
 +	} //end for
 +	//merge all areas
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +//		Log_Print("checking area %d\n", i);
 +		//if the area is invalid
 +		if (tmparea->invalid)
 +		{
 +//			Log_Print("   area invalid\n");
 +			continue;
 +		} //end if
 +		//
 +		for (face = tmparea->tmpfaces; face; face = face->next[side])
 +		{
 +			side = (face->frontarea != tmparea);
 +			//if the face has both a front and back area
 +			if (face->frontarea && face->backarea)
 +			{
 +//				Log_Print("  checking area %d with %d\n", face->frontarea, face->backarea);
 +				if (AAS_TryMergeFaceAreas(face))
 +				{
 +					qprintf("\r%6d", ++nummerges);
 +					break;
 +				} //end if
 +			} //end if
 +		} //end for
 +	} //end for
 +	Log_Print("\r%6d areas merged\n", nummerges);
 +	//refresh the merged tree
 +	AAS_RefreshMergedTree_r(tmpaasworld.nodes);
 +} //end of the function AAS_MergeAreas*/
 +
 +int AAS_GroundArea(tmp_area_t *tmparea)
 +{
 +	tmp_face_t *face;
 +	int side;
 +
 +	for (face = tmparea->tmpfaces; face; face = face->next[side])
 +	{
 +		side = (face->frontarea != tmparea);
 +		if (face->faceflags & FACE_GROUND) return true;
 +	} //end for
 +	return false;
 +} //end of the function AAS_GroundArea
 +
 +void AAS_MergeAreas(void)
 +{
 +	int side, nummerges, merges, groundfirst;
 +	tmp_area_t *tmparea, *othertmparea;
 +	tmp_face_t *face;
 +
 +	nummerges = 0;
 +	Log_Write("AAS_MergeAreas\r\n");
 +	qprintf("%6d areas merged", 1);
 +	//
 +	groundfirst = true;
 +	//for (i = 0; i < 4 || merges; i++)
 +	while(1)
 +	{
 +		//if (i < 2) groundfirst = true;
 +		//else groundfirst = false;
 +		//
 +		merges = 0;
 +		//first merge grounded areas only
 +		for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +		{
 +			//if the area is invalid
 +			if (tmparea->invalid)
 +			{
 +				continue;
 +			} //end if
 +			//
 +			if (groundfirst)
 +			{
 +				if (!AAS_GroundArea(tmparea)) continue;
 +			} //end if
 +			//
 +			for (face = tmparea->tmpfaces; face; face = face->next[side])
 +			{
 +				side = (face->frontarea != tmparea);
 +				//if the face has both a front and back area
 +				if (face->frontarea && face->backarea)
 +				{
 +					//
 +					if (face->frontarea == tmparea) othertmparea = face->backarea;
 +					else othertmparea = face->frontarea;
 +					//
 +					if (groundfirst)
 +					{
 +						if (!AAS_GroundArea(othertmparea)) continue;
 +					} //end if
 +					if (AAS_TryMergeFaceAreas(face))
 +					{
 +						qprintf("\r%6d", ++nummerges);
 +						merges++;
 +						break;
 +					} //end if
 +				} //end if
 +			} //end for
 +		} //end for
 +		if (!merges)
 +		{
 +			if (groundfirst) groundfirst = false;
 +			else break;
 +		} //end if
 +	} //end for
 +	qprintf("\n");
 +	Log_Write("%6d areas merged\r\n", nummerges);
 +	//refresh the merged tree
 +	AAS_RefreshMergedTree_r(tmpaasworld.nodes);
 +} //end of the function AAS_MergeAreas
 diff --git a/code/bspc/aas_areamerging.h b/code/bspc/aas_areamerging.h new file mode 100755 index 0000000..c0b39a4 --- /dev/null +++ b/code/bspc/aas_areamerging.h @@ -0,0 +1,24 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void AAS_MergeAreas(void);
 +
 diff --git a/code/bspc/aas_cfg.c b/code/bspc/aas_cfg.c new file mode 100755 index 0000000..3aeb569 --- /dev/null +++ b/code/bspc/aas_cfg.c @@ -0,0 +1,252 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "float.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_store.h"
 +#include "aas_cfg.h"
 +#include "../botlib/l_precomp.h"
 +#include "../botlib/l_struct.h"
 +#include "../botlib/l_libvar.h"
 +
 +//structure field offsets
 +#define BBOX_OFS(x) (int)&(((aas_bbox_t *)0)->x)
 +#define CFG_OFS(x) (int)&(((cfg_t *)0)->x)
 +
 +//bounding box definition
 +fielddef_t bbox_fields[] =
 +{
 +	{"presencetype", BBOX_OFS(presencetype), FT_INT},
 +	{"flags", BBOX_OFS(flags), FT_INT},
 +	{"mins", BBOX_OFS(mins), FT_FLOAT|FT_ARRAY, 3},
 +	{"maxs", BBOX_OFS(maxs), FT_FLOAT|FT_ARRAY, 3},
 +	{NULL, 0, 0, 0}
 +};
 +
 +fielddef_t cfg_fields[] =
 +{
 +	{"phys_gravitydirection", CFG_OFS(phys_gravitydirection), FT_FLOAT|FT_ARRAY, 3},
 +	{"phys_friction", CFG_OFS(phys_friction), FT_FLOAT},
 +	{"phys_stopspeed", CFG_OFS(phys_stopspeed), FT_FLOAT},
 +	{"phys_gravity", CFG_OFS(phys_gravity), FT_FLOAT},
 +	{"phys_waterfriction", CFG_OFS(phys_waterfriction), FT_FLOAT},
 +	{"phys_watergravity", CFG_OFS(phys_watergravity), FT_FLOAT},
 +	{"phys_maxvelocity", CFG_OFS(phys_maxvelocity), FT_FLOAT},
 +	{"phys_maxwalkvelocity", CFG_OFS(phys_maxwalkvelocity), FT_FLOAT},
 +	{"phys_maxcrouchvelocity", CFG_OFS(phys_maxcrouchvelocity), FT_FLOAT},
 +	{"phys_maxswimvelocity", CFG_OFS(phys_maxswimvelocity), FT_FLOAT},
 +	{"phys_walkaccelerate", CFG_OFS(phys_walkaccelerate), FT_FLOAT},
 +	{"phys_airaccelerate", CFG_OFS(phys_airaccelerate), FT_FLOAT},
 +	{"phys_swimaccelerate", CFG_OFS(phys_swimaccelerate), FT_FLOAT},
 +	{"phys_maxstep", CFG_OFS(phys_maxstep), FT_FLOAT},
 +	{"phys_maxsteepness", CFG_OFS(phys_maxsteepness), FT_FLOAT},
 +	{"phys_maxwaterjump", CFG_OFS(phys_maxwaterjump), FT_FLOAT},
 +	{"phys_maxbarrier", CFG_OFS(phys_maxbarrier), FT_FLOAT},
 +	{"phys_jumpvel", CFG_OFS(phys_jumpvel), FT_FLOAT},
 +	{"phys_falldelta5", CFG_OFS(phys_falldelta5), FT_FLOAT},
 +	{"phys_falldelta10", CFG_OFS(phys_falldelta10), FT_FLOAT},
 +	{"rs_waterjump", CFG_OFS(rs_waterjump), FT_FLOAT},
 +	{"rs_teleport", CFG_OFS(rs_teleport), FT_FLOAT},
 +	{"rs_barrierjump", CFG_OFS(rs_barrierjump), FT_FLOAT},
 +	{"rs_startcrouch", CFG_OFS(rs_startcrouch), FT_FLOAT},
 +	{"rs_startgrapple", CFG_OFS(rs_startgrapple), FT_FLOAT},
 +	{"rs_startwalkoffledge", CFG_OFS(rs_startwalkoffledge), FT_FLOAT},
 +	{"rs_startjump", CFG_OFS(rs_startjump), FT_FLOAT},
 +	{"rs_rocketjump", CFG_OFS(rs_rocketjump), FT_FLOAT},
 +	{"rs_bfgjump", CFG_OFS(rs_bfgjump), FT_FLOAT},
 +	{"rs_jumppad", CFG_OFS(rs_jumppad), FT_FLOAT},
 +	{"rs_aircontrolledjumppad", CFG_OFS(rs_aircontrolledjumppad), FT_FLOAT},
 +	{"rs_funcbob", CFG_OFS(rs_funcbob), FT_FLOAT},
 +	{"rs_startelevator", CFG_OFS(rs_startelevator), FT_FLOAT},
 +	{"rs_falldamage5", CFG_OFS(rs_falldamage5), FT_FLOAT},
 +	{"rs_falldamage10", CFG_OFS(rs_falldamage10), FT_FLOAT},
 +	{"rs_maxjumpfallheight", CFG_OFS(rs_maxjumpfallheight), FT_FLOAT},
 +	{NULL, 0, 0, 0}
 +};
 +
 +structdef_t bbox_struct =
 +{
 +	sizeof(aas_bbox_t), bbox_fields
 +};
 +structdef_t cfg_struct =
 +{
 +	sizeof(cfg_t), cfg_fields
 +};
 +
 +//global cfg
 +cfg_t cfg;
 +
 +//===========================================================================
 +// the default Q3A configuration
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void DefaultCfg(void)
 +{
 +	int i;
 +
 +	// default all float values to infinite
 +	for (i = 0; cfg_fields[i].name; i++)
 +	{
 +		if ((cfg_fields[i].type & FT_TYPE) == FT_FLOAT)
 +			*(float *)( ((char*)&cfg) + cfg_fields[i].offset ) = FLT_MAX;
 +	} //end for
 +	//
 +	cfg.numbboxes = 2;
 +	//bbox 0
 +	cfg.bboxes[0].presencetype = PRESENCE_NORMAL;
 +	cfg.bboxes[0].flags = 0;
 +	cfg.bboxes[0].mins[0] = -15;
 +	cfg.bboxes[0].mins[1] = -15;
 +	cfg.bboxes[0].mins[2] = -24;
 +	cfg.bboxes[0].maxs[0] = 15;
 +	cfg.bboxes[0].maxs[1] = 15;
 +	cfg.bboxes[0].maxs[2] = 32;
 +	//bbox 1
 +	cfg.bboxes[1].presencetype = PRESENCE_CROUCH;
 +	cfg.bboxes[1].flags = 1;
 +	cfg.bboxes[1].mins[0] = -15;
 +	cfg.bboxes[1].mins[1] = -15;
 +	cfg.bboxes[1].mins[2] = -24;
 +	cfg.bboxes[1].maxs[0] = 15;
 +	cfg.bboxes[1].maxs[1] = 15;
 +	cfg.bboxes[1].maxs[2] = 16;
 +	//
 +	cfg.allpresencetypes = PRESENCE_NORMAL|PRESENCE_CROUCH;
 +	cfg.phys_gravitydirection[0]	= 0;
 +	cfg.phys_gravitydirection[1]	= 0;
 +	cfg.phys_gravitydirection[2]	= -1;
 +	cfg.phys_maxsteepness			= 0.7;
 +} //end of the function DefaultCfg
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +char	* QDECL va( char *format, ... )
 +{
 +	va_list		argptr;
 +	static char		string[2][32000];	// in case va is called by nested functions
 +	static int		index = 0;
 +	char	*buf;
 +
 +	buf = string[index & 1];
 +	index++;
 +
 +	va_start (argptr, format);
 +	vsprintf (buf, format,argptr);
 +	va_end (argptr);
 +
 +	return buf;
 +} //end of the function va
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void SetCfgLibVars(void)
 +{
 +	int i;
 +	float value;
 +
 +	for (i = 0; cfg_fields[i].name; i++)
 +	{
 +		if ((cfg_fields[i].type & FT_TYPE) == FT_FLOAT)
 +		{
 +			value = *(float *)(((char*)&cfg) + cfg_fields[i].offset);
 +			if (value != FLT_MAX)
 +			{
 +				LibVarSet(cfg_fields[i].name, va("%f", value));
 +			} //end if
 +		} //end if
 +	} //end for
 +} //end of the function SetCfgLibVars
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int LoadCfgFile(char *filename)
 +{
 +	source_t *source;
 +	token_t token;
 +	int settingsdefined;
 +
 +	source = LoadSourceFile(filename);
 +	if (!source)
 +	{
 +		Log_Print("couldn't open cfg file %s\n", filename);
 +		return false;
 +	} //end if
 +
 +	settingsdefined = false;
 +	memset(&cfg, 0, sizeof(cfg_t));
 +
 +	while(PC_ReadToken(source, &token))
 +	{
 +		if (!stricmp(token.string, "bbox"))
 +		{
 +			if (cfg.numbboxes >= AAS_MAX_BBOXES)
 +			{
 +				SourceError(source, "too many bounding box volumes defined");
 +			} //end if
 +			if (!ReadStructure(source, &bbox_struct, (char *) &cfg.bboxes[cfg.numbboxes]))
 +			{
 +				FreeSource(source);
 +				return false;
 +			} //end if
 +			cfg.allpresencetypes |= cfg.bboxes[cfg.numbboxes].presencetype;
 +			cfg.numbboxes++;
 +		} //end if
 +		else if (!stricmp(token.string, "settings"))
 +		{
 +			if (settingsdefined)
 +			{
 +				SourceWarning(source, "settings already defined\n");
 +			} //end if
 +			settingsdefined = true;
 +			if (!ReadStructure(source, &cfg_struct, (char *) &cfg))
 +			{
 +				FreeSource(source);
 +				return false;
 +			} //end if
 +		} //end else if
 +	} //end while
 +	if (VectorLength(cfg.phys_gravitydirection) < 0.9 || VectorLength(cfg.phys_gravitydirection) > 1.1)
 +	{
 +		SourceError(source, "invalid gravity direction specified");
 +	} //end if
 +	if (cfg.numbboxes <= 0)
 +	{
 +		SourceError(source, "no bounding volumes specified");
 +	} //end if
 +	FreeSource(source);
 +	SetCfgLibVars();
 +	Log_Print("using cfg file %s\n", filename);
 +	return true;
 +} //end of the function LoadCfgFile
 diff --git a/code/bspc/aas_cfg.h b/code/bspc/aas_cfg.h new file mode 100755 index 0000000..25537c6 --- /dev/null +++ b/code/bspc/aas_cfg.h @@ -0,0 +1,73 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#define BBOXFL_GROUNDED			1	//bounding box only valid when on ground
 +#define BBOXFL_NOTGROUNDED		2	//bounding box only valid when NOT on ground
 +
 +typedef struct cfg_s
 +{
 +	int numbboxes;						//number of bounding boxes
 +	aas_bbox_t bboxes[AAS_MAX_BBOXES];	//all the bounding boxes
 +	int allpresencetypes;				//or of all presence types
 +	// aas settings
 +	vec3_t phys_gravitydirection;
 +	float phys_friction;
 +	float phys_stopspeed;
 +	float phys_gravity;
 +	float phys_waterfriction;
 +	float phys_watergravity;
 +	float phys_maxvelocity;
 +	float phys_maxwalkvelocity;
 +	float phys_maxcrouchvelocity;
 +	float phys_maxswimvelocity;
 +	float phys_walkaccelerate;
 +	float phys_airaccelerate;
 +	float phys_swimaccelerate;
 +	float phys_maxstep;
 +	float phys_maxsteepness;
 +	float phys_maxwaterjump;
 +	float phys_maxbarrier;
 +	float phys_jumpvel;
 +	float phys_falldelta5;
 +	float phys_falldelta10;
 +	float rs_waterjump;
 +	float rs_teleport;
 +	float rs_barrierjump;
 +	float rs_startcrouch;
 +	float rs_startgrapple;
 +	float rs_startwalkoffledge;
 +	float rs_startjump;
 +	float rs_rocketjump;
 +	float rs_bfgjump;
 +	float rs_jumppad;
 +	float rs_aircontrolledjumppad;
 +	float rs_funcbob;
 +	float rs_startelevator;
 +	float rs_falldamage5;
 +	float rs_falldamage10;
 +	float rs_maxjumpfallheight;
 +} cfg_t;
 +
 +extern cfg_t cfg;
 +
 +void DefaultCfg(void);
 +int LoadCfgFile(char *filename);
 diff --git a/code/bspc/aas_create.c b/code/bspc/aas_create.c new file mode 100755 index 0000000..914b25b --- /dev/null +++ b/code/bspc/aas_create.c @@ -0,0 +1,1142 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_create.h"
 +#include "aas_store.h"
 +#include "aas_gsubdiv.h"
 +#include "aas_facemerging.h"
 +#include "aas_areamerging.h"
 +#include "aas_edgemelting.h"
 +#include "aas_prunenodes.h"
 +#include "aas_cfg.h"
 +#include "../game/surfaceflags.h"
 +
 +//#define AW_DEBUG
 +//#define L_DEBUG
 +
 +#define AREAONFACESIDE(face, area)		(face->frontarea != area)
 +
 +tmp_aas_t tmpaasworld;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_InitTmpAAS(void)
 +{
 +	//tmp faces
 +	tmpaasworld.numfaces = 0;
 +	tmpaasworld.facenum = 0;
 +	tmpaasworld.faces = NULL;
 +	//tmp convex areas
 +	tmpaasworld.numareas = 0;
 +	tmpaasworld.areanum = 0;
 +	tmpaasworld.areas = NULL;
 +	//tmp nodes
 +	tmpaasworld.numnodes = 0;
 +	tmpaasworld.nodes = NULL;
 +	//
 +	tmpaasworld.nodebuffer = NULL;
 +} //end of the function AAS_InitTmpAAS
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FreeTmpAAS(void)
 +{
 +	tmp_face_t *f, *nextf;
 +	tmp_area_t *a, *nexta;
 +	tmp_nodebuf_t *nb, *nextnb;
 +
 +	//free all the faces
 +	for (f = tmpaasworld.faces; f; f = nextf)
 +	{
 +		nextf = f->l_next;
 +		if (f->winding) FreeWinding(f->winding);
 +		FreeMemory(f);
 +	} //end if
 +	//free all tmp areas
 +	for (a = tmpaasworld.areas; a; a = nexta)
 +	{
 +		nexta = a->l_next;
 +		if (a->settings) FreeMemory(a->settings);
 +		FreeMemory(a);
 +	} //end for
 +	//free all the tmp nodes
 +	for (nb = tmpaasworld.nodebuffer; nb; nb = nextnb)
 +	{
 +		nextnb = nb->next;
 +		FreeMemory(nb);
 +	} //end for
 +} //end of the function AAS_FreeTmpAAS
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_face_t *AAS_AllocTmpFace(void)
 +{
 +	tmp_face_t *tmpface;
 +
 +	tmpface = (tmp_face_t *) GetClearedMemory(sizeof(tmp_face_t));
 +	tmpface->num = tmpaasworld.facenum++;
 +	tmpface->l_prev = NULL;
 +	tmpface->l_next = tmpaasworld.faces;
 +	if (tmpaasworld.faces) tmpaasworld.faces->l_prev = tmpface;
 +	tmpaasworld.faces = tmpface;
 +	tmpaasworld.numfaces++;
 +	return tmpface;
 +} //end of the function AAS_AllocTmpFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FreeTmpFace(tmp_face_t *tmpface)
 +{
 +	if (tmpface->l_next) tmpface->l_next->l_prev = tmpface->l_prev;
 +	if (tmpface->l_prev) tmpface->l_prev->l_next = tmpface->l_next;
 +	else tmpaasworld.faces = tmpface->l_next;
 +	//free the winding
 +	if (tmpface->winding) FreeWinding(tmpface->winding);
 +	//free the face
 +	FreeMemory(tmpface);
 +	tmpaasworld.numfaces--;
 +} //end of the function AAS_FreeTmpFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_area_t *AAS_AllocTmpArea(void)
 +{
 +	tmp_area_t *tmparea;
 +
 +	tmparea = (tmp_area_t *) GetClearedMemory(sizeof(tmp_area_t));
 +	tmparea->areanum = tmpaasworld.areanum++;
 +	tmparea->l_prev = NULL;
 +	tmparea->l_next = tmpaasworld.areas;
 +	if (tmpaasworld.areas) tmpaasworld.areas->l_prev = tmparea;
 +	tmpaasworld.areas = tmparea;
 +	tmpaasworld.numareas++;
 +	return tmparea;
 +} //end of the function AAS_AllocTmpArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FreeTmpArea(tmp_area_t *tmparea)
 +{
 +	if (tmparea->l_next) tmparea->l_next->l_prev = tmparea->l_prev;
 +	if (tmparea->l_prev) tmparea->l_prev->l_next = tmparea->l_next;
 +	else tmpaasworld.areas = tmparea->l_next;
 +	if (tmparea->settings) FreeMemory(tmparea->settings);
 +	FreeMemory(tmparea);
 +	tmpaasworld.numareas--;
 +} //end of the function AAS_FreeTmpArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_AllocTmpNode(void)
 +{
 +	tmp_nodebuf_t *nodebuf;
 +
 +	if (!tmpaasworld.nodebuffer ||
 +			tmpaasworld.nodebuffer->numnodes >= NODEBUF_SIZE)
 +	{
 +		nodebuf = (tmp_nodebuf_t *) GetClearedMemory(sizeof(tmp_nodebuf_t));
 +		nodebuf->next = tmpaasworld.nodebuffer;
 +		nodebuf->numnodes = 0;
 +		tmpaasworld.nodebuffer = nodebuf;
 +	} //end if
 +	tmpaasworld.numnodes++;
 +	return &tmpaasworld.nodebuffer->nodes[tmpaasworld.nodebuffer->numnodes++];
 +} //end of the function AAS_AllocTmpNode
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FreeTmpNode(tmp_node_t *tmpnode)
 +{
 +	tmpaasworld.numnodes--;
 +} //end of the function AAS_FreeTmpNode
 +//===========================================================================
 +// returns true if the face is a gap from the given side
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_GapFace(tmp_face_t *tmpface, int side)
 +{
 +	vec3_t invgravity;
 +
 +	//if the face is a solid or ground face it can't be a gap
 +	if (tmpface->faceflags & (FACE_GROUND | FACE_SOLID)) return 0;
 +
 +	VectorCopy(cfg.phys_gravitydirection, invgravity);
 +	VectorInverse(invgravity);
 +
 +	return (DotProduct(invgravity, mapplanes[tmpface->planenum ^ side].normal) > cfg.phys_maxsteepness);
 +} //end of the function AAS_GapFace
 +//===========================================================================
 +// returns true if the face is a ground face
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_GroundFace(tmp_face_t *tmpface)
 +{
 +	vec3_t invgravity;
 +
 +	//must be a solid face
 +	if (!(tmpface->faceflags & FACE_SOLID)) return 0;
 +
 +	VectorCopy(cfg.phys_gravitydirection, invgravity);
 +	VectorInverse(invgravity);
 +
 +	return (DotProduct(invgravity, mapplanes[tmpface->planenum].normal) > cfg.phys_maxsteepness);
 +} //end of the function AAS_GroundFace
 +//===========================================================================
 +// adds the side of a face to an area
 +//
 +// side :	0 = front side
 +//				1 = back side
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_AddFaceSideToArea(tmp_face_t *tmpface, int side, tmp_area_t *tmparea)
 +{
 +	int tmpfaceside;
 +
 +	if (side)
 +	{
 +		if (tmpface->backarea) Error("AAS_AddFaceSideToArea: already a back area\n");
 +	} //end if
 +	else
 +	{
 +		if (tmpface->frontarea) Error("AAS_AddFaceSideToArea: already a front area\n");
 +	} //end else
 +
 +	if (side) tmpface->backarea = tmparea;
 +	else tmpface->frontarea = tmparea;
 +
 +	if (tmparea->tmpfaces)
 +	{
 +		tmpfaceside = tmparea->tmpfaces->frontarea != tmparea;
 +		tmparea->tmpfaces->prev[tmpfaceside] = tmpface;
 +	} //end if
 +	tmpface->next[side] = tmparea->tmpfaces;
 +	tmpface->prev[side] = NULL;
 +	tmparea->tmpfaces = tmpface;
 +} //end of the function AAS_AddFaceSideToArea
 +//===========================================================================
 +// remove (a side of) a face from an area
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_RemoveFaceFromArea(tmp_face_t *tmpface, tmp_area_t *tmparea)
 +{
 +	int side, prevside, nextside;
 +
 +	if (tmpface->frontarea != tmparea &&
 +			tmpface->backarea != tmparea)
 +	{
 +		Error("AAS_RemoveFaceFromArea: face not part of the area");
 +	} //end if
 +	side = tmpface->frontarea != tmparea;
 +	if (tmpface->prev[side])
 +	{
 +		prevside = tmpface->prev[side]->frontarea != tmparea;
 +		tmpface->prev[side]->next[prevside] = tmpface->next[side];
 +	} //end if
 +	else
 +	{
 +		tmparea->tmpfaces = tmpface->next[side];
 +	} //end else
 +	if (tmpface->next[side])
 +	{
 +		nextside = tmpface->next[side]->frontarea != tmparea;
 +		tmpface->next[side]->prev[nextside] = tmpface->prev[side];
 +	} //end if
 +	//remove the area number from the face depending on the side
 +	if (side) tmpface->backarea = NULL;
 +	else tmpface->frontarea = NULL;
 +	tmpface->prev[side] = NULL;
 +	tmpface->next[side] = NULL;
 +} //end of the function AAS_RemoveFaceFromArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CheckArea(tmp_area_t *tmparea)
 +{
 +	int side;
 +	tmp_face_t *face;
 +	plane_t *plane;
 +	vec3_t wcenter, acenter = {0, 0, 0};
 +	vec3_t normal;
 +	float n, dist;
 +
 +	if (tmparea->invalid) Log_Print("AAS_CheckArea: invalid area\n");
 +	for (n = 0, face = tmparea->tmpfaces; face; face = face->next[side])
 +	{
 +		//side of the face the area is on
 +		side = face->frontarea != tmparea;
 +		WindingCenter(face->winding, wcenter);
 +		VectorAdd(acenter, wcenter, acenter);
 +		n++;
 +	} //end for
 +	n = 1 / n;
 +	VectorScale(acenter, n, acenter);
 +	for (face = tmparea->tmpfaces; face; face = face->next[side])
 +	{
 +		//side of the face the area is on
 +		side = face->frontarea != tmparea;
 +
 +#ifdef L_DEBUG
 +		if (WindingError(face->winding))
 +		{
 +			Log_Write("AAS_CheckArea: area %d face %d: %s\r\n", tmparea->areanum,
 +						face->num, WindingErrorString());
 +		} //end if
 +#endif L_DEBUG
 +
 +		plane = &mapplanes[face->planenum ^ side];
 +
 +		if (DotProduct(plane->normal, acenter) - plane->dist < 0)
 +		{
 +			Log_Print("AAS_CheckArea: area %d face %d is flipped\n", tmparea->areanum, face->num);
 +			Log_Print("AAS_CheckArea: area %d center is %f %f %f\n", tmparea->areanum, acenter[0], acenter[1], acenter[2]);
 +		} //end if
 +		//check if the winding plane is the same as the face plane
 +		WindingPlane(face->winding, normal, &dist);
 +		plane = &mapplanes[face->planenum];
 +#ifdef L_DEBUG
 +		if (fabs(dist - plane->dist) > 0.4 ||
 +				fabs(normal[0] - plane->normal[0]) > 0.0001 ||
 +				fabs(normal[1] - plane->normal[1]) > 0.0001 ||
 +				fabs(normal[2] - plane->normal[2]) > 0.0001)
 +		{
 +			Log_Write("AAS_CheckArea: area %d face %d winding plane unequal to face plane\r\n",
 +										tmparea->areanum, face->num);
 +		} //end if
 +#endif L_DEBUG
 +	} //end for
 +} //end of the function AAS_CheckArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CheckFaceWindingPlane(tmp_face_t *face)
 +{
 +	float dist, sign1, sign2;
 +	vec3_t normal;
 +	plane_t *plane;
 +	winding_t *w;
 +
 +	//check if the winding plane is the same as the face plane
 +	WindingPlane(face->winding, normal, &dist);
 +	plane = &mapplanes[face->planenum];
 +	//
 +	sign1 = DotProduct(plane->normal, normal);
 +	//
 +	if (fabs(dist - plane->dist) > 0.4 ||
 +			fabs(normal[0] - plane->normal[0]) > 0.0001 ||
 +			fabs(normal[1] - plane->normal[1]) > 0.0001 ||
 +			fabs(normal[2] - plane->normal[2]) > 0.0001)
 +	{
 +		VectorInverse(normal);
 +		dist = -dist;
 +		if (fabs(dist - plane->dist) > 0.4 ||
 +				fabs(normal[0] - plane->normal[0]) > 0.0001 ||
 +				fabs(normal[1] - plane->normal[1]) > 0.0001 ||
 +				fabs(normal[2] - plane->normal[2]) > 0.0001)
 +		{
 +			Log_Write("AAS_CheckFaceWindingPlane: face %d winding plane unequal to face plane\r\n",
 +									face->num);
 +			//
 +			sign2 = DotProduct(plane->normal, normal);
 +			if ((sign1 < 0 && sign2 > 0) ||
 +					(sign1 > 0 && sign2 < 0))
 +			{
 +				Log_Write("AAS_CheckFaceWindingPlane: face %d winding reversed\r\n",
 +									face->num);
 +				w = face->winding;
 +				face->winding = ReverseWinding(w);
 +				FreeWinding(w);
 +			} //end if
 +		} //end if
 +		else
 +		{
 +			Log_Write("AAS_CheckFaceWindingPlane: face %d winding reversed\r\n",
 +									face->num);
 +			w = face->winding;
 +			face->winding = ReverseWinding(w);
 +			FreeWinding(w);
 +		} //end else
 +	} //end if
 +} //end of the function AAS_CheckFaceWindingPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CheckAreaWindingPlanes(void)
 +{
 +	int side;
 +	tmp_area_t *tmparea;
 +	tmp_face_t *face;
 +
 +	Log_Write("AAS_CheckAreaWindingPlanes:\r\n");
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +		if (tmparea->invalid) continue;
 +		for (face = tmparea->tmpfaces; face; face = face->next[side])
 +		{
 +			side = face->frontarea != tmparea;
 +			AAS_CheckFaceWindingPlane(face);
 +		} //end for
 +	} //end for
 +} //end of the function AAS_CheckAreaWindingPlanes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FlipAreaFaces(tmp_area_t *tmparea)
 +{
 +	int side;
 +	tmp_face_t *face;
 +	plane_t *plane;
 +	vec3_t wcenter, acenter = {0, 0, 0};
 +	//winding_t *w;
 +	float n;
 +
 +	for (n = 0, face = tmparea->tmpfaces; face; face = face->next[side])
 +	{
 +		if (!face->frontarea) Error("face %d has no front area\n", face->num);
 +		//side of the face the area is on
 +		side = face->frontarea != tmparea;
 +		WindingCenter(face->winding, wcenter);
 +		VectorAdd(acenter, wcenter, acenter);
 +		n++;
 +	} //end for
 +	n = 1 / n;
 +	VectorScale(acenter, n, acenter);
 +	for (face = tmparea->tmpfaces; face; face = face->next[side])
 +	{
 +		//side of the face the area is on
 +		side = face->frontarea != tmparea;
 +
 +		plane = &mapplanes[face->planenum ^ side];
 +
 +		if (DotProduct(plane->normal, acenter) - plane->dist < 0)
 +		{
 +			Log_Print("area %d face %d flipped: front area %d, back area %d\n", tmparea->areanum, face->num,
 +					face->frontarea ? face->frontarea->areanum : 0,
 +					face->backarea ? face->backarea->areanum : 0);
 +			/*
 +			face->planenum = face->planenum ^ 1;
 +			w = face->winding;
 +			face->winding = ReverseWinding(w);
 +			FreeWinding(w);
 +			*/
 +		} //end if
 +#ifdef L_DEBUG
 +		{
 +			float dist;
 +			vec3_t normal;
 +
 +			//check if the winding plane is the same as the face plane
 +			WindingPlane(face->winding, normal, &dist);
 +			plane = &mapplanes[face->planenum];
 +			if (fabs(dist - plane->dist) > 0.4 ||
 +					fabs(normal[0] - plane->normal[0]) > 0.0001 ||
 +					fabs(normal[1] - plane->normal[1]) > 0.0001 ||
 +					fabs(normal[2] - plane->normal[2]) > 0.0001)
 +			{
 +				Log_Write("area %d face %d winding plane unequal to face plane\r\n",
 +											tmparea->areanum, face->num);
 +			} //end if
 +		}
 +#endif
 +	} //end for
 +} //end of the function AAS_FlipAreaFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_RemoveAreaFaceColinearPoints(void)
 +{
 +	int side;
 +	tmp_face_t *face;
 +	tmp_area_t *tmparea;
 +
 +	//FIXME: loop over the faces instead of area->faces
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +		for (face = tmparea->tmpfaces; face; face = face->next[side])
 +		{
 +			side = face->frontarea != tmparea;
 +			RemoveColinearPoints(face->winding);
 +//			RemoveEqualPoints(face->winding, 0.1);
 +		} //end for
 +	} //end for
 +} //end of the function AAS_RemoveAreaFaceColinearPoints
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_RemoveTinyFaces(void)
 +{
 +	int side, num;
 +	tmp_face_t *face, *nextface;
 +	tmp_area_t *tmparea;
 +
 +	//FIXME: loop over the faces instead of area->faces
 +	Log_Write("AAS_RemoveTinyFaces\r\n");
 +	num = 0;
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +		for (face = tmparea->tmpfaces; face; face = nextface)
 +		{
 +			side = face->frontarea != tmparea;
 +			nextface = face->next[side];
 +			//
 +			if (WindingArea(face->winding) < 1)
 +			{
 +				if (face->frontarea) AAS_RemoveFaceFromArea(face, face->frontarea);
 +				if (face->backarea) AAS_RemoveFaceFromArea(face, face->backarea);
 +				AAS_FreeTmpFace(face);
 +				//Log_Write("area %d face %d is tiny\r\n", tmparea->areanum, face->num);
 +				num++;
 +			} //end if
 +		} //end for
 +	} //end for
 +	Log_Write("%d tiny faces removed\r\n", num);
 +} //end of the function AAS_RemoveTinyFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CreateAreaSettings(void)
 +{
 +	int i, flags, side, numgrounded, numladderareas, numliquidareas;
 +	tmp_face_t *face;
 +	tmp_area_t *tmparea;
 +
 +	numgrounded = 0;
 +	numladderareas = 0;
 +	numliquidareas = 0;
 +	Log_Write("AAS_CreateAreaSettings\r\n");
 +	i = 0;
 +	qprintf("%6d areas provided with settings", i);
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +		//if the area is invalid there no need to create settings for it
 +		if (tmparea->invalid) continue;
 +
 +		tmparea->settings = (tmp_areasettings_t *) GetClearedMemory(sizeof(tmp_areasettings_t));
 +		tmparea->settings->contents = tmparea->contents;
 +		tmparea->settings->modelnum = tmparea->modelnum;
 +		flags = 0;
 +		for (face = tmparea->tmpfaces; face; face = face->next[side])
 +		{
 +			side = face->frontarea != tmparea;
 +			flags |= face->faceflags;
 +		} //end for
 +		tmparea->settings->areaflags = 0;
 +		if (flags & FACE_GROUND)
 +		{
 +			tmparea->settings->areaflags |= AREA_GROUNDED;
 +			numgrounded++;
 +		} //end if
 +		if (flags & FACE_LADDER)
 +		{
 +			tmparea->settings->areaflags |= AREA_LADDER;
 +			numladderareas++;
 +		} //end if
 +		if (tmparea->contents & (AREACONTENTS_WATER |
 +											AREACONTENTS_SLIME |
 +											AREACONTENTS_LAVA))
 +		{
 +			tmparea->settings->areaflags |= AREA_LIQUID;
 +			numliquidareas++;
 +		} //end if
 +		//presence type of the area
 +		tmparea->settings->presencetype = tmparea->presencetype;
 +		//
 +		qprintf("\r%6d", ++i);
 +	} //end for
 +	qprintf("\n");
 +#ifdef AASINFO
 +	Log_Print("%6d grounded areas\n", numgrounded);
 +	Log_Print("%6d ladder areas\n", numladderareas);
 +	Log_Print("%6d liquid areas\n", numliquidareas);
 +#endif //AASINFO
 +} //end of the function AAS_CreateAreaSettings
 +//===========================================================================
 +// create a tmp AAS area from a leaf node
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_CreateArea(node_t *node)
 +{
 +	int pside;
 +	int areafaceflags;
 +	portal_t	*p;
 +	tmp_face_t *tmpface;
 +	tmp_area_t *tmparea;
 +	tmp_node_t *tmpnode;
 +	vec3_t up = {0, 0, 1};
 +
 +	//create an area from this leaf
 +	tmparea = AAS_AllocTmpArea();
 +	tmparea->tmpfaces = NULL;
 +	//clear the area face flags
 +	areafaceflags = 0;
 +	//make aas faces from the portals
 +	for (p = node->portals; p; p = p->next[pside])
 +	{
 +		pside = (p->nodes[1] == node);
 +		//don't create faces from very small portals
 +//		if (WindingArea(p->winding) < 1) continue;
 +		//if there's already a face created for this portal
 +		if (p->tmpface)
 +		{
 +			//add the back side of the face to the area
 +			AAS_AddFaceSideToArea(p->tmpface, 1, tmparea);
 +		} //end if
 +		else
 +		{
 +			tmpface = AAS_AllocTmpFace();
 +			//set the face pointer at the portal so we can see from
 +			//the portal there's a face created for it
 +			p->tmpface = tmpface;
 +			//FIXME: test this change
 +			//tmpface->planenum = (p->planenum & ~1) | pside;
 +			tmpface->planenum = p->planenum ^ pside;
 +			if (pside) tmpface->winding = ReverseWinding(p->winding);
 +			else tmpface->winding = CopyWinding(p->winding);
 +#ifdef L_DEBUG
 +			//
 +			AAS_CheckFaceWindingPlane(tmpface);
 +#endif //L_DEBUG
 +			//if there's solid at the other side of the portal
 +			if (p->nodes[!pside]->contents & (CONTENTS_SOLID | CONTENTS_PLAYERCLIP))
 +			{
 +				tmpface->faceflags |= FACE_SOLID;
 +			} //end if
 +			//else there is no solid at the other side and if there
 +			//is a liquid at this side
 +			else if (node->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))
 +			{
 +				tmpface->faceflags |= FACE_LIQUID;
 +				//if there's no liquid at the other side
 +				if (!(p->nodes[!pside]->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)))
 +				{
 +					tmpface->faceflags |= FACE_LIQUIDSURFACE;
 +				} //end if
 +			} //end else
 +			//if there's ladder contents at other side of the portal
 +			if ((p->nodes[pside]->contents & CONTENTS_LADDER) ||
 +					(p->nodes[!pside]->contents & CONTENTS_LADDER))
 +			{
 +
 +				//NOTE: doesn't have to be solid at the other side because
 +				// when standing one can use a crouch area (which is not solid)
 +				// as a ladder
 +				// imagine a ladder one can walk underthrough,
 +				// under the ladder against the ladder is a crouch area
 +				// the (vertical) sides of this crouch area area also used as
 +				// ladder sides when standing (not crouched)
 +				tmpface->faceflags |= FACE_LADDER;
 +			} //end if
 +			//if it is possible to stand on the face
 +			if (AAS_GroundFace(tmpface))
 +			{
 +				tmpface->faceflags |= FACE_GROUND;
 +			} //end if
 +			//
 +			areafaceflags |= tmpface->faceflags;
 +			//no aas face number yet (zero is a dummy in the aasworld faces)
 +			tmpface->aasfacenum = 0;
 +			//add the front side of the face to the area
 +			AAS_AddFaceSideToArea(tmpface, 0, tmparea);
 +		} //end else
 +	} //end for
 +	qprintf("\r%6d", tmparea->areanum);
 +	//presence type in the area
 +	tmparea->presencetype = ~node->expansionbboxes & cfg.allpresencetypes;
 +	//
 +	tmparea->contents = 0;
 +	if (node->contents & CONTENTS_CLUSTERPORTAL) tmparea->contents |= AREACONTENTS_CLUSTERPORTAL;
 +	if (node->contents & CONTENTS_MOVER) tmparea->contents |= AREACONTENTS_MOVER;
 +	if (node->contents & CONTENTS_TELEPORTER) tmparea->contents |= AREACONTENTS_TELEPORTER;
 +	if (node->contents & CONTENTS_JUMPPAD) tmparea->contents |= AREACONTENTS_JUMPPAD;
 +	if (node->contents & CONTENTS_DONOTENTER) tmparea->contents |= AREACONTENTS_DONOTENTER;
 +	if (node->contents & CONTENTS_WATER) tmparea->contents |= AREACONTENTS_WATER;
 +	if (node->contents & CONTENTS_LAVA) tmparea->contents |= AREACONTENTS_LAVA;
 +	if (node->contents & CONTENTS_SLIME) tmparea->contents |= AREACONTENTS_SLIME;
 +	if (node->contents & CONTENTS_NOTTEAM1) tmparea->contents |= AREACONTENTS_NOTTEAM1;
 +	if (node->contents & CONTENTS_NOTTEAM2) tmparea->contents |= AREACONTENTS_NOTTEAM2;
 +
 +	//store the bsp model that's inside this node
 +	tmparea->modelnum = node->modelnum;
 +	//sorta check for flipped area faces (remove??)
 +	AAS_FlipAreaFaces(tmparea);
 +	//check if the area is ok (remove??)
 +	AAS_CheckArea(tmparea);
 +	//
 +	tmpnode = AAS_AllocTmpNode();
 +	tmpnode->planenum = 0;
 +	tmpnode->children[0] = 0;
 +	tmpnode->children[1] = 0;
 +	tmpnode->tmparea = tmparea;
 +	//
 +	return tmpnode;
 +} //end of the function AAS_CreateArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_CreateAreas_r(node_t *node)
 +{
 +	tmp_node_t *tmpnode;
 +
 +	//recurse down to leafs
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		//the first tmp node is a dummy
 +		tmpnode = AAS_AllocTmpNode();
 +		tmpnode->planenum = node->planenum;
 +		tmpnode->children[0] = AAS_CreateAreas_r(node->children[0]);
 +		tmpnode->children[1] = AAS_CreateAreas_r(node->children[1]);
 +		return tmpnode;
 +	} //end if
 +	//areas won't be created for solid leafs
 +	if (node->contents & CONTENTS_SOLID)
 +	{
 +		//just return zero for a solid leaf (in tmp AAS NULL is a solid leaf)
 +		return NULL;
 +	} //end if
 +
 +	return AAS_CreateArea(node);
 +} //end of the function AAS_CreateAreas_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CreateAreas(node_t *node)
 +{
 +	Log_Write("AAS_CreateAreas\r\n");
 +	qprintf("%6d areas created", 0);
 +	tmpaasworld.nodes = AAS_CreateAreas_r(node);
 +	qprintf("\n");
 +	Log_Write("%6d areas created\r\n", tmpaasworld.numareas);
 +} //end of the function AAS_CreateAreas
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_PrintNumGroundFaces(void)
 +{
 +	tmp_face_t *tmpface;
 +	int numgroundfaces = 0;
 +
 +	for (tmpface = tmpaasworld.faces; tmpface; tmpface = tmpface->l_next)
 +	{
 +		if (tmpface->faceflags & FACE_GROUND)
 +		{
 +			numgroundfaces++;
 +		} //end if
 +	} //end for
 +	qprintf("%6d ground faces\n", numgroundfaces);
 +} //end of the function AAS_PrintNumGroundFaces
 +//===========================================================================
 +// checks the number of shared faces between the given two areas
 +// since areas are convex they should only have ONE shared face
 +// however due to crappy face merging there are sometimes several
 +// shared faces
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CheckAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2)
 +{
 +	int numsharedfaces, side;
 +	tmp_face_t *face1, *sharedface;
 +
 +	if (tmparea1->invalid || tmparea2->invalid) return;
 +
 +	sharedface = NULL;
 +	numsharedfaces = 0;
 +	for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side])
 +	{
 +		side = face1->frontarea != tmparea1;
 +		if (face1->backarea == tmparea2 || face1->frontarea == tmparea2)
 +		{
 +			sharedface = face1;
 +			numsharedfaces++;
 +		} //end if
 +	} //end if
 +	if (!sharedface) return;
 +	//the areas should only have one shared face
 +	if (numsharedfaces > 1)
 +	{
 +		Log_Write("---- tmp area %d and %d have %d shared faces\r\n",
 +									tmparea1->areanum, tmparea2->areanum, numsharedfaces);
 +		for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side])
 +		{
 +			side = face1->frontarea != tmparea1;
 +			if (face1->backarea == tmparea2 || face1->frontarea == tmparea2)
 +			{
 +				Log_Write("face %d, planenum = %d, face->frontarea = %d face->backarea = %d\r\n",
 +								face1->num, face1->planenum, face1->frontarea->areanum, face1->backarea->areanum);
 +			} //end if
 +		} //end if
 +	} //end if
 +} //end of the function AAS_CheckAreaSharedFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CheckSharedFaces(void)
 +{
 +	tmp_area_t *tmparea1, *tmparea2;
 +
 +	for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next)
 +	{
 +		for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next)
 +		{
 +			if (tmparea1 == tmparea2) continue;
 +			AAS_CheckAreaSharedFaces(tmparea1, tmparea2);
 +		} //end for
 +	} //end for
 +} //end of the function AAS_CheckSharedFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FlipFace(tmp_face_t *face)
 +{
 +	tmp_area_t *frontarea, *backarea;
 +	winding_t *w;
 +
 +	frontarea = face->frontarea;
 +	backarea = face->backarea;
 +	//must have an area at both sides before flipping is allowed
 +	if (!frontarea || !backarea) return;
 +	//flip the face winding
 +	w = face->winding;
 +	face->winding = ReverseWinding(w);
 +	FreeWinding(w);
 +	//flip the face plane
 +	face->planenum ^= 1;
 +	//flip the face areas
 +	AAS_RemoveFaceFromArea(face, frontarea);
 +	AAS_RemoveFaceFromArea(face, backarea);
 +	AAS_AddFaceSideToArea(face, 1, frontarea);
 +	AAS_AddFaceSideToArea(face, 0, backarea);
 +} //end of the function AAS_FlipFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +/*
 +void AAS_FlipAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2)
 +{
 +	int numsharedfaces, side, area1facing, area2facing;
 +	tmp_face_t *face1, *sharedface;
 +
 +	if (tmparea1->invalid || tmparea2->invalid) return;
 +
 +	sharedface = NULL;
 +	numsharedfaces = 0;
 +	area1facing = 0;		//number of shared faces facing towards area 1
 +	area2facing = 0;		//number of shared faces facing towards area 2
 +	for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side])
 +	{
 +		side = face1->frontarea != tmparea1;
 +		if (face1->backarea == tmparea2 || face1->frontarea == tmparea2)
 +		{
 +			sharedface = face1;
 +			numsharedfaces++;
 +			if (face1->frontarea == tmparea1) area1facing++;
 +			else area2facing++;
 +		} //end if
 +	} //end if
 +	if (!sharedface) return;
 +	//if there's only one shared face
 +	if (numsharedfaces <= 1) return;
 +	//if all the shared faces are facing to the same area
 +	if (numsharedfaces == area1facing || numsharedfaces == area2facing) return;
 +	//
 +	do
 +	{
 +		for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side])
 +		{
 +			side = face1->frontarea != tmparea1;
 +			if (face1->backarea == tmparea2 || face1->frontarea == tmparea2)
 +			{
 +				if (face1->frontarea != tmparea1)
 +				{
 +					AAS_FlipFace(face1);
 +					break;
 +				} //end if
 +			} //end if
 +		} //end for
 +	} while(face1);
 +} //end of the function AAS_FlipAreaSharedFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FlipSharedFaces(void)
 +{
 +	int i;
 +	tmp_area_t *tmparea1, *tmparea2;
 +
 +	i = 0;
 +	qprintf("%6d areas checked for shared face flipping", i);
 +	for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next)
 +	{
 +		if (tmparea1->invalid) continue;
 +		for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next)
 +		{
 +			if (tmparea2->invalid) continue;
 +			if (tmparea1 == tmparea2) continue;
 +			AAS_FlipAreaSharedFaces(tmparea1, tmparea2);
 +		} //end for
 +		qprintf("\r%6d", ++i);
 +	} //end for
 +	Log_Print("\r%6d areas checked for shared face flipping\n", i);
 +} //end of the function AAS_FlipSharedFaces
 +*/
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FlipSharedFaces(void)
 +{
 +	int i, side1, side2;
 +	tmp_area_t *tmparea1;
 +	tmp_face_t *face1, *face2;
 +
 +	i = 0;
 +	qprintf("%6d areas checked for shared face flipping", i);
 +	for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next)
 +	{
 +		if (tmparea1->invalid) continue;
 +		for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1])
 +		{
 +			side1 = face1->frontarea != tmparea1;
 +			if (!face1->frontarea || !face1->backarea) continue;
 +			//
 +			for (face2 = face1->next[side1]; face2; face2 = face2->next[side2])
 +			{
 +				side2 = face2->frontarea != tmparea1;
 +				if (!face2->frontarea || !face2->backarea) continue;
 +				//
 +				if (face1->frontarea == face2->backarea &&
 +					face1->backarea == face2->frontarea)
 +				{
 +					AAS_FlipFace(face2);
 +				} //end if
 +				//recheck side
 +				side2 = face2->frontarea != tmparea1;
 +			} //end for
 +		} //end for
 +		qprintf("\r%6d", ++i);
 +	} //end for
 +	qprintf("\n");
 +	Log_Write("%6d areas checked for shared face flipping\r\n", i);
 +} //end of the function AAS_FlipSharedFaces
 +//===========================================================================
 +// creates an .AAS file with the given name
 +// a MAP should be loaded before calling this
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_Create(char *aasfile)
 +{
 +	entity_t	*e;
 +	tree_t *tree;
 +	double start_time;
 +
 +	//for a possible leak file
 +	strcpy(source, aasfile);
 +	StripExtension(source);
 +	//the time started
 +	start_time = I_FloatTime();
 +	//set the default number of threads (depends on number of processors)
 +	ThreadSetDefault();
 +	//set the global entity number to the world model
 +	entity_num = 0;
 +	//the world entity
 +	e = &entities[entity_num];
 +	//process the whole world
 +	tree = ProcessWorldBrushes(e->firstbrush, e->firstbrush + e->numbrushes);
 +	//if the conversion is cancelled
 +	if (cancelconversion)
 +	{
 +		Tree_Free(tree);
 +		return;
 +	} //end if
 +	//display BSP tree creation time
 +	Log_Print("BSP tree created in %5.0f seconds\n", I_FloatTime() - start_time);
 +	//prune the bsp tree
 +	Tree_PruneNodes(tree->headnode);
 +	//if the conversion is cancelled
 +	if (cancelconversion)
 +	{
 +		Tree_Free(tree);
 +		return;
 +	} //end if
 +	//create the tree portals
 +	MakeTreePortals(tree);
 +	//if the conversion is cancelled
 +	if (cancelconversion)
 +	{
 +		Tree_Free(tree);
 +		return;
 +	} //end if
 +	//Marks all nodes that can be reached by entites
 +	if (FloodEntities(tree))
 +	{
 +		//fill out nodes that can't be reached
 +		FillOutside(tree->headnode);
 +	} //end if
 +	else
 +	{
 +		LeakFile(tree);
 +		Error("**** leaked ****\n");
 +		return;
 +	} //end else
 +	//create AAS from the BSP tree
 +	//==========================================
 +	//initialize tmp aas
 +	AAS_InitTmpAAS();
 +	//create the convex areas from the leaves
 +	AAS_CreateAreas(tree->headnode);
 +	//free the BSP tree because it isn't used anymore
 +	if (freetree) Tree_Free(tree);
 +	//try to merge area faces
 +	AAS_MergeAreaFaces();
 +	//do gravitational subdivision
 +	AAS_GravitationalSubdivision();
 +	//merge faces if possible
 +	AAS_MergeAreaFaces();
 +	AAS_RemoveAreaFaceColinearPoints();
 +	//merge areas if possible
 +	AAS_MergeAreas();
 +	//NOTE: prune nodes directly after area merging
 +	AAS_PruneNodes();
 +	//flip shared faces so they are all facing to the same area
 +	AAS_FlipSharedFaces();
 +	AAS_RemoveAreaFaceColinearPoints();
 +	//merge faces if possible
 +	AAS_MergeAreaFaces();
 +	//merge area faces in the same plane
 +	AAS_MergeAreaPlaneFaces();
 +	//do ladder subdivision
 +	AAS_LadderSubdivision();
 +	//FIXME: melting is buggy
 +	AAS_MeltAreaFaceWindings();
 +	//remove tiny faces
 +	AAS_RemoveTinyFaces();
 +	//create area settings
 +	AAS_CreateAreaSettings();
 +	//check if the winding plane is equal to the face plane
 +	//AAS_CheckAreaWindingPlanes();
 +	//
 +	//AAS_CheckSharedFaces();
 +	//==========================================
 +	//if the conversion is cancelled
 +	if (cancelconversion)
 +	{
 +		Tree_Free(tree);
 +		AAS_FreeTmpAAS();
 +		return;
 +	} //end if
 +	//store the created AAS stuff in the AAS file format and write the file
 +	AAS_StoreFile(aasfile);
 +	//free the temporary AAS memory
 +	AAS_FreeTmpAAS();
 +	//display creation time
 +	Log_Print("\nAAS created in %5.0f seconds\n", I_FloatTime() - start_time);
 +} //end of the function AAS_Create
 diff --git a/code/bspc/aas_create.h b/code/bspc/aas_create.h new file mode 100755 index 0000000..cc5693c --- /dev/null +++ b/code/bspc/aas_create.h @@ -0,0 +1,136 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#define AREA_PORTAL			1
 +
 +//temporary AAS face
 +typedef struct tmp_face_s
 +{
 +	int num;						//face number
 +	int planenum;					//number of the plane the face is in
 +	winding_t *winding;				//winding of the face
 +	struct tmp_area_s *frontarea;	//area at the front of the face
 +	struct tmp_area_s *backarea;	//area at the back of the face
 +	int faceflags;					//flags of this face
 +	int aasfacenum;					//the number of the aas face used for this face
 +	//double link list pointers for front and back area
 +	struct tmp_face_s *prev[2], *next[2];
 +	//links in the list with faces
 +	struct tmp_face_s *l_prev, *l_next;
 +} tmp_face_t;
 +
 +//temporary AAS area settings
 +typedef struct tmp_areasettings_s
 +{
 +	//could also add all kind of statistic fields
 +	int contents;					//contents of the area
 +	int modelnum;					//bsp model inside this area
 +	int areaflags;					//area flags
 +	int presencetype;				//how a bot can be present in this area
 +	int numreachableareas;			//number of reachable areas from this one
 +	int firstreachablearea;			//first reachable area in the reachable area index
 +} tmp_areasettings_t;
 +
 +//temporary AAS area
 +typedef struct tmp_area_s
 +{
 +	int areanum;						//number of the area
 +	struct tmp_face_s *tmpfaces;		//the faces of the area
 +	int presencetype;					//presence type of the area
 +	int contents;						//area contents
 +	int modelnum;						//bsp model inside this area
 +	int invalid;						//true if the area is invalid
 +	tmp_areasettings_t *settings;		//area settings
 +	struct tmp_area_s *mergedarea;		//points to the new area after merging
 +										//when mergedarea != 0 the area has only the
 +										//seperating face of the merged areas
 +	int aasareanum;						//number of the aas area created for this tmp area
 +	//links in the list with areas
 +	struct tmp_area_s *l_prev, *l_next;
 +} tmp_area_t;
 +
 +//temporary AAS node
 +typedef struct tmp_node_s
 +{
 +	int planenum;					//node plane number
 +	struct tmp_area_s *tmparea;		//points to an area if this node is an area
 +	struct tmp_node_s *children[2];	//child nodes of this node
 +} tmp_node_t;
 +
 +#define NODEBUF_SIZE			128
 +//node buffer
 +typedef struct tmp_nodebuf_s
 +{
 +	int numnodes;
 +	struct tmp_nodebuf_s *next;
 +	tmp_node_t nodes[NODEBUF_SIZE];
 +} tmp_nodebuf_t;
 +
 +//the whole temorary AAS
 +typedef struct tmp_aas_s
 +{
 +	//faces
 +	int numfaces;
 +	int facenum;
 +	tmp_face_t *faces;
 +	//areas
 +	int numareas;
 +	int areanum;
 +	tmp_area_t *areas;
 +	//area settings
 +	int numareasettings;
 +	tmp_areasettings_t *areasettings;
 +	//nodes
 +	int numnodes;
 +	tmp_node_t *nodes;
 +	//node buffer
 +	tmp_nodebuf_t *nodebuffer;
 +} tmp_aas_t;
 +
 +extern tmp_aas_t tmpaasworld;
 +
 +//creates a .AAS file with the given name from an already loaded map
 +void AAS_Create(char *aasfile);
 +//adds a face side to an area
 +void AAS_AddFaceSideToArea(tmp_face_t *tmpface, int side, tmp_area_t *tmparea);
 +//remvoes a face from an area
 +void AAS_RemoveFaceFromArea(tmp_face_t *tmpface, tmp_area_t *tmparea);
 +//allocate a tmp face
 +tmp_face_t *AAS_AllocTmpFace(void);
 +//free the tmp face
 +void AAS_FreeTmpFace(tmp_face_t *tmpface);
 +//allocate a tmp area
 +tmp_area_t *AAS_AllocTmpArea(void);
 +//free a tmp area
 +void AAS_FreeTmpArea(tmp_area_t *tmparea);
 +//allocate a tmp node
 +tmp_node_t *AAS_AllocTmpNode(void);
 +//free a tmp node
 +void AAS_FreeTmpNode(tmp_node_t *node);
 +//checks if an area is ok
 +void AAS_CheckArea(tmp_area_t *tmparea);
 +//flips the area faces where needed
 +void AAS_FlipAreaFaces(tmp_area_t *tmparea);
 +//returns true if the face is a gap seen from the given side
 +int AAS_GapFace(tmp_face_t *tmpface, int side);
 +//returns true if the face is a ground face
 +int AAS_GroundFace(tmp_face_t *tmpface);
 diff --git a/code/bspc/aas_edgemelting.c b/code/bspc/aas_edgemelting.c new file mode 100755 index 0000000..e84f48b --- /dev/null +++ b/code/bspc/aas_edgemelting.c @@ -0,0 +1,108 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_create.h"
 +
 +//===========================================================================
 +// try to melt the windings of the two faces
 +// FIXME: this is buggy
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_MeltFaceWinding(tmp_face_t *face1, tmp_face_t *face2)
 +{
 +	int i, n;
 +	int splits = 0;
 +	winding_t *w2, *neww;
 +	plane_t *plane1;
 +
 +#ifdef DEBUG
 +	if (!face1->winding) Error("face1 %d without winding", face1->num);
 +	if (!face2->winding) Error("face2 %d without winding", face2->num);
 +#endif //DEBUG
 +	w2 = face2->winding;
 +	plane1 = &mapplanes[face1->planenum];
 +	for (i = 0; i < w2->numpoints; i++)
 +	{
 +		if (PointOnWinding(face1->winding, plane1->normal, plane1->dist, w2->p[i], &n))
 +		{
 +			neww = AddWindingPoint(face1->winding, w2->p[i], n);
 +			FreeWinding(face1->winding);
 +			face1->winding = neww;
 +
 +			splits++;
 +		} //end if
 +	} //end for
 +	return splits;
 +} //end of the function AAS_MeltFaceWinding
 +//===========================================================================
 +// melt the windings of the area faces
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_MeltFaceWindingsOfArea(tmp_area_t *tmparea)
 +{
 +	int side1, side2, num_windingsplits = 0;
 +	tmp_face_t *face1, *face2;
 +
 +	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		side1 = face1->frontarea != tmparea;
 +		for (face2 = tmparea->tmpfaces; face2; face2 = face2->next[side2])
 +		{
 +			side2 = face2->frontarea != tmparea;
 +			if (face1 == face2) continue;
 +			num_windingsplits += AAS_MeltFaceWinding(face1, face2);
 +		} //end for
 +	} //end for
 +	return num_windingsplits;
 +} //end of the function AAS_MeltFaceWindingsOfArea
 +//===========================================================================
 +// melt the windings of the faces of all areas
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_MeltAreaFaceWindings(void)
 +{
 +	tmp_area_t *tmparea;
 +	int num_windingsplits = 0;
 +
 +	Log_Write("AAS_MeltAreaFaceWindings\r\n");
 +	qprintf("%6d edges melted", num_windingsplits);
 +	//NOTE: first convex area (zero) is a dummy
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +		num_windingsplits += AAS_MeltFaceWindingsOfArea(tmparea);
 +		qprintf("\r%6d", num_windingsplits);
 +	} //end for
 +	qprintf("\n");
 +	Log_Write("%6d edges melted\r\n", num_windingsplits);
 +} //end of the function AAS_MeltAreaFaceWindings
 +
 diff --git a/code/bspc/aas_edgemelting.h b/code/bspc/aas_edgemelting.h new file mode 100755 index 0000000..4c03e97 --- /dev/null +++ b/code/bspc/aas_edgemelting.h @@ -0,0 +1,24 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void AAS_MeltAreaFaceWindings(void);
 +
 diff --git a/code/bspc/aas_facemerging.c b/code/bspc/aas_facemerging.c new file mode 100755 index 0000000..bf170de --- /dev/null +++ b/code/bspc/aas_facemerging.c @@ -0,0 +1,282 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_create.h"
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_TryMergeFaces(tmp_face_t *face1, tmp_face_t *face2)
 +{
 +	winding_t *neww;
 +
 +#ifdef DEBUG
 +	if (!face1->winding) Error("face1 %d without winding", face1->num);
 +	if (!face2->winding) Error("face2 %d without winding", face2->num);
 +#endif //DEBUG
 +	//
 +	if (face1->faceflags != face2->faceflags) return false;
 +	//NOTE: if the front or back area is zero this doesn't mean there's
 +	//a real area. It means there's solid at that side of the face
 +	//if both faces have the same front area
 +	if (face1->frontarea == face2->frontarea)
 +	{
 +		//if both faces have the same back area
 +		if (face1->backarea == face2->backarea)
 +		{
 +			//if the faces are in the same plane
 +			if (face1->planenum == face2->planenum)
 +			{
 +				//if they have both a front and a back area (no solid on either side)
 +				if (face1->frontarea && face1->backarea)
 +				{
 +					neww = MergeWindings(face1->winding, face2->winding,
 +								mapplanes[face1->planenum].normal);
 +				} //end if
 +				else
 +				{
 +					//this function is to be found in l_poly.c
 +					neww = TryMergeWinding(face1->winding, face2->winding,
 +									mapplanes[face1->planenum].normal);
 +				} //end else
 +				if (neww)
 +				{
 +					FreeWinding(face1->winding);
 +					face1->winding = neww;
 +					if (face2->frontarea) AAS_RemoveFaceFromArea(face2, face2->frontarea);
 +					if (face2->backarea) AAS_RemoveFaceFromArea(face2, face2->backarea);
 +					AAS_FreeTmpFace(face2);
 +					return true;
 +				} //end if
 +			} //end if
 +			else if ((face1->planenum & ~1) == (face2->planenum & ~1))
 +			{
 +				Log_Write("face %d and %d, same front and back area but flipped planes\r\n",
 +							face1->num, face2->num);
 +			} //end if
 +		} //end if
 +	} //end if
 +	return false;
 +} //end of the function AAS_TryMergeFaces
 +/*
 +int AAS_TryMergeFaces(tmp_face_t *face1, tmp_face_t *face2)
 +{
 +	winding_t *neww;
 +
 +#ifdef DEBUG
 +	if (!face1->winding) Error("face1 %d without winding", face1->num);
 +	if (!face2->winding) Error("face2 %d without winding", face2->num);
 +#endif //DEBUG
 +	//if the faces are in the same plane
 +	if ((face1->planenum & ~1) != (face2->planenum & ~1)) return false;
 +//	if (face1->planenum != face2->planenum) return false;
 +	//NOTE: if the front or back area is zero this doesn't mean there's
 +	//a real area. It means there's solid at that side of the face
 +	//if both faces have the same front area
 +	if (face1->frontarea != face2->frontarea ||
 +		face1->backarea != face2->backarea)
 +	{
 +		if (!face1->frontarea || !face1->backarea ||
 +				!face2->frontarea || !face2->backarea) return false;
 +		else if (face1->frontarea != face2->backarea ||
 +					face1->backarea != face2->frontarea) return false;
 +//		return false;
 +	} //end if
 +	//this function is to be found in l_poly.c
 +	neww = TryMergeWinding(face1->winding, face2->winding,
 +					mapplanes[face1->planenum].normal);
 +	if (!neww) return false;
 +	//
 +	FreeWinding(face1->winding);
 +	face1->winding = neww;
 +	//remove face2
 +	if (face2->frontarea)
 +		AAS_RemoveFaceFromArea(face2, &tmpaasworld.areas[face2->frontarea]);
 +	if (face2->backarea)
 +		AAS_RemoveFaceFromArea(face2, &tmpaasworld.areas[face2->backarea]);
 +	return true;
 +} //end of the function AAS_TryMergeFaces*/
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_MergeAreaFaces(void)
 +{
 +	int num_facemerges = 0;
 +	int side1, side2, restart;
 +	tmp_area_t *tmparea, *lasttmparea;
 +	tmp_face_t *face1, *face2;
 +
 +	Log_Write("AAS_MergeAreaFaces\r\n");
 +	qprintf("%6d face merges", num_facemerges);
 +	//NOTE: first convex area is a dummy
 +	lasttmparea = tmpaasworld.areas;
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next)
 +	{
 +		restart = false;
 +		//
 +		if (tmparea->invalid) continue;
 +		//
 +		for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +		{
 +			side1 = face1->frontarea != tmparea;
 +			for (face2 = face1->next[side1]; face2; face2 = face2->next[side2])
 +			{
 +				side2 = face2->frontarea != tmparea;
 +				//if succesfully merged
 +				if (AAS_TryMergeFaces(face1, face2))
 +				{
 +					//start over again after merging two faces
 +					restart = true;
 +					num_facemerges++;
 +					qprintf("\r%6d", num_facemerges);
 +					AAS_CheckArea(tmparea);
 +					break;
 +				} //end if
 +			} //end for
 +			if (restart)
 +			{
 +				tmparea = lasttmparea;
 +				break;
 +			} //end if
 +		} //end for
 +		lasttmparea = tmparea;
 +	} //end for
 +	qprintf("\n");
 +	Log_Write("%6d face merges\r\n", num_facemerges);
 +} //end of the function AAS_MergeAreaFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_MergePlaneFaces(tmp_area_t *tmparea, int planenum)
 +{
 +	tmp_face_t *face1, *face2, *nextface2;
 +	winding_t *neww;
 +	int side1, side2;
 +
 +	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		side1 = face1->frontarea != tmparea;
 +		if (face1->planenum != planenum) continue;
 +		//
 +		for (face2 = face1->next[side1]; face2; face2 = nextface2)
 +		{
 +			side2 = face2->frontarea != tmparea;
 +			nextface2 = face2->next[side2];
 +			//
 +			if ((face2->planenum & ~1) != (planenum & ~1)) continue;
 +			//
 +			neww = MergeWindings(face1->winding, face2->winding,
 +								mapplanes[face1->planenum].normal);
 +			FreeWinding(face1->winding);
 +			face1->winding = neww;
 +			if (face2->frontarea) AAS_RemoveFaceFromArea(face2, face2->frontarea);
 +			if (face2->backarea) AAS_RemoveFaceFromArea(face2, face2->backarea);
 +			AAS_FreeTmpFace(face2);
 +			//
 +			nextface2 = face1->next[side1];
 +		} //end for
 +	} //end for
 +} //end of the function AAS_MergePlaneFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_CanMergePlaneFaces(tmp_area_t *tmparea, int planenum)
 +{
 +	tmp_area_t *frontarea, *backarea;
 +	tmp_face_t *face1;
 +	int side1, merge, faceflags;
 +
 +	frontarea = backarea = NULL;
 +	merge = false;
 +	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		side1 = face1->frontarea != tmparea;
 +		if ((face1->planenum & ~1) != (planenum & ~1)) continue;
 +		if (!frontarea && !backarea)
 +		{
 +			frontarea = face1->frontarea;
 +			backarea = face1->backarea;
 +			faceflags = face1->faceflags;
 +		} //end if
 +		else
 +		{
 +			if (frontarea != face1->frontarea) return false;
 +			if (backarea != face1->backarea) return false;
 +			if (faceflags != face1->faceflags) return false;
 +			merge = true;
 +		} //end else
 +	} //end for
 +	return merge;
 +} //end of the function AAS_CanMergePlaneFaces
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_MergeAreaPlaneFaces(void)
 +{
 +	int num_facemerges = 0;
 +	int side1;
 +	tmp_area_t *tmparea, *nexttmparea;
 +	tmp_face_t *face1;
 +
 +	Log_Write("AAS_MergePlaneFaces\r\n");
 +	qprintf("%6d plane face merges", num_facemerges);
 +	//NOTE: first convex area is a dummy
 +	for (tmparea = tmpaasworld.areas; tmparea; tmparea = nexttmparea)
 +	{
 +		nexttmparea = tmparea->l_next;
 +		//
 +		if (tmparea->invalid) continue;
 +		//
 +		for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +		{
 +			side1 = face1->frontarea != tmparea;
 +			//
 +			if (AAS_CanMergePlaneFaces(tmparea, face1->planenum))
 +			{
 +				AAS_MergePlaneFaces(tmparea, face1->planenum);
 +				nexttmparea = tmparea;
 +				num_facemerges++;
 +				qprintf("\r%6d", num_facemerges);
 +				break;
 +			} //end if
 +		} //end for
 +	} //end for
 +	qprintf("\n");
 +	Log_Write("%6d plane face merges\r\n", num_facemerges);
 +} //end of the function AAS_MergeAreaPlaneFaces
 diff --git a/code/bspc/aas_facemerging.h b/code/bspc/aas_facemerging.h new file mode 100755 index 0000000..5a81735 --- /dev/null +++ b/code/bspc/aas_facemerging.h @@ -0,0 +1,24 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void AAS_MergeAreaFaces(void);
 +void AAS_MergeAreaPlaneFaces(void);
 diff --git a/code/bspc/aas_file.c b/code/bspc/aas_file.c new file mode 100755 index 0000000..9f41639 --- /dev/null +++ b/code/bspc/aas_file.c @@ -0,0 +1,549 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_file.h"
 +#include "aas_store.h"
 +#include "aas_create.h"
 +
 +#define AAS_Error			Error
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_SwapAASData(void)
 +{
 +	int i, j;
 +	//bounding boxes
 +	for (i = 0; i < aasworld.numbboxes; i++)
 +	{
 +		aasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype);
 +		aasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags);
 +		for (j = 0; j < 3; j++)
 +		{
 +			aasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]);
 +			aasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]);
 +		} //end for
 +	} //end for
 +	//vertexes
 +	for (i = 0; i < aasworld.numvertexes; i++)
 +	{
 +		for (j = 0; j < 3; j++)
 +			aasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]);
 +	} //end for
 +	//planes
 +	for (i = 0; i < aasworld.numplanes; i++)
 +	{
 +		for (j = 0; j < 3; j++)
 +			aasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]);
 +		aasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist);
 +		aasworld.planes[i].type = LittleLong(aasworld.planes[i].type);
 +	} //end for
 +	//edges
 +	for (i = 0; i < aasworld.numedges; i++)
 +	{
 +		aasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]);
 +		aasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]);
 +	} //end for
 +	//edgeindex
 +	for (i = 0; i < aasworld.edgeindexsize; i++)
 +	{
 +		aasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]);
 +	} //end for
 +	//faces
 +	for (i = 0; i < aasworld.numfaces; i++)
 +	{
 +		aasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum);
 +		aasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags);
 +		aasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges);
 +		aasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge);
 +		aasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea);
 +		aasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea);
 +	} //end for
 +	//face index
 +	for (i = 0; i < aasworld.faceindexsize; i++)
 +	{
 +		aasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]);
 +	} //end for
 +	//convex areas
 +	for (i = 0; i < aasworld.numareas; i++)
 +	{
 +		aasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum);
 +		aasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces);
 +		aasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface);
 +		for (j = 0; j < 3; j++)
 +		{
 +			aasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]);
 +			aasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]);
 +			aasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]);
 +		} //end for
 +	} //end for
 +	//area settings
 +	for (i = 0; i < aasworld.numareasettings; i++)
 +	{
 +		aasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents);
 +		aasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags);
 +		aasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype);
 +		aasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster);
 +		aasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum);
 +		aasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas);
 +		aasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea);
 +	} //end for
 +	//area reachability
 +	for (i = 0; i < aasworld.reachabilitysize; i++)
 +	{
 +		aasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum);
 +		aasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum);
 +		aasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum);
 +		for (j = 0; j < 3; j++)
 +		{
 +			aasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]);
 +			aasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]);
 +		} //end for
 +		aasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype);
 +		aasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime);
 +	} //end for
 +	//nodes
 +	for (i = 0; i < aasworld.numnodes; i++)
 +	{
 +		aasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum);
 +		aasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]);
 +		aasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]);
 +	} //end for
 +	//cluster portals
 +	for (i = 0; i < aasworld.numportals; i++)
 +	{
 +		aasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum);
 +		aasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster);
 +		aasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster);
 +		aasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]);
 +		aasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]);
 +	} //end for
 +	//cluster portal index
 +	for (i = 0; i < aasworld.portalindexsize; i++)
 +	{
 +		aasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]);
 +	} //end for
 +	//cluster
 +	for (i = 0; i < aasworld.numclusters; i++)
 +	{
 +		aasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas);
 +		aasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals);
 +		aasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal);
 +	} //end for
 +} //end of the function AAS_SwapAASData
 +//===========================================================================
 +// dump the current loaded aas file
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_DumpAASData(void)
 +{
 +	/*
 +	if (aasworld.vertexes) FreeMemory(aasworld.vertexes);
 +	aasworld.vertexes = NULL;
 +	if (aasworld.planes) FreeMemory(aasworld.planes);
 +	aasworld.planes = NULL;
 +	if (aasworld.edges) FreeMemory(aasworld.edges);
 +	aasworld.edges = NULL;
 +	if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);
 +	aasworld.edgeindex = NULL;
 +	if (aasworld.faces) FreeMemory(aasworld.faces);
 +	aasworld.faces = NULL;
 +	if (aasworld.faceindex) FreeMemory(aasworld.faceindex);
 +	aasworld.faceindex = NULL;
 +	if (aasworld.areas) FreeMemory(aasworld.areas);
 +	aasworld.areas = NULL;
 +	if (aasworld.areasettings) FreeMemory(aasworld.areasettings);
 +	aasworld.areasettings = NULL;
 +	if (aasworld.reachability) FreeMemory(aasworld.reachability);
 +	aasworld.reachability = NULL;
 +	*/
 +	aasworld.loaded = false;
 +} //end of the function AAS_DumpAASData
 +//===========================================================================
 +// allocate memory and read a lump of a AAS file
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +char *AAS_LoadAASLump(FILE *fp, int offset, int length, void *buf)
 +{
 +	if (!length)
 +	{
 +		printf("lump size 0\n");
 +		return buf;
 +	} //end if
 +	//seek to the data
 +	if (fseek(fp, offset, SEEK_SET))
 +	{
 +		AAS_Error("can't seek to lump\n");
 +		AAS_DumpAASData();
 +		fclose(fp);
 +		return 0;
 +	} //end if
 +	//allocate memory
 +	if (!buf) buf = (void *) GetClearedMemory(length);
 +	//read the data
 +	if (fread((char *) buf, 1, length, fp) != length)
 +	{
 +		AAS_Error("can't read lump\n");
 +		FreeMemory(buf);
 +		AAS_DumpAASData();
 +		fclose(fp);
 +		return NULL;
 +	} //end if
 +	return buf;
 +} //end of the function AAS_LoadAASLump
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_DData(unsigned char *data, int size)
 +{
 +	int i;
 +
 +	for (i = 0; i < size; i++)
 +	{
 +		data[i] ^= (unsigned char) i * 119;
 +	} //end for
 +} //end of the function AAS_DData
 +//===========================================================================
 +// load an aas file
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_LoadAASFile(char *filename, int fpoffset, int fplength)
 +{
 +	FILE *fp;
 +	aas_header_t header;
 +	int offset, length;
 +
 +	//dump current loaded aas file
 +	AAS_DumpAASData();
 +	//open the file
 +	fp = fopen(filename, "rb");
 +	if (!fp)
 +	{
 +		AAS_Error("can't open %s\n", filename);
 +		return false;
 +	} //end if
 +	//seek to the correct position (in the pak file)
 +	if (fseek(fp, fpoffset, SEEK_SET))
 +	{
 +		AAS_Error("can't seek to file %s\n");
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//read the header
 +	if (fread(&header, sizeof(aas_header_t), 1, fp) != 1)
 +	{
 +		AAS_Error("can't read header of file %s\n", filename);
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//check header identification
 +	header.ident = LittleLong(header.ident);
 +	if (header.ident != AASID)
 +	{
 +		AAS_Error("%s is not an AAS file\n", filename);
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//check the version
 +	header.version = LittleLong(header.version);
 +	if (header.version != AASVERSION_OLD && header.version != AASVERSION)
 +	{
 +		AAS_Error("%s is version %i, not %i\n", filename, header.version, AASVERSION);
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//
 +	if (header.version == AASVERSION)
 +	{
 +		AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);
 +	} //end if
 +	aasworld.bspchecksum = LittleLong(header.bspchecksum);
 +	//load the lumps:
 +	//bounding boxes
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_BBOXES].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_BBOXES].filelen);
 +	aasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, aasworld.bboxes);
 +	if (!aasworld.bboxes) return false;
 +	aasworld.numbboxes = length / sizeof(aas_bbox_t);
 +	//vertexes
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen);
 +	aasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.vertexes);
 +	if (!aasworld.vertexes) return false;
 +	aasworld.numvertexes = length / sizeof(aas_vertex_t);
 +	//planes
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_PLANES].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_PLANES].filelen);
 +	aasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, aasworld.planes);
 +	if (!aasworld.planes) return false;
 +	aasworld.numplanes = length / sizeof(aas_plane_t);
 +	//edges
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_EDGES].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_EDGES].filelen);
 +	aasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, aasworld.edges);
 +	if (!aasworld.edges) return false;
 +	aasworld.numedges = length / sizeof(aas_edge_t);
 +	//edgeindex
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen);
 +	aasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.edgeindex);
 +	if (!aasworld.edgeindex) return false;
 +	aasworld.edgeindexsize = length / sizeof(aas_edgeindex_t);
 +	//faces
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_FACES].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_FACES].filelen);
 +	aasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, aasworld.faces);
 +	if (!aasworld.faces) return false;
 +	aasworld.numfaces = length / sizeof(aas_face_t);
 +	//faceindex
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen);
 +	aasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.faceindex);
 +	if (!aasworld.faceindex) return false;
 +	aasworld.faceindexsize = length / sizeof(int);
 +	//convex areas
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_AREAS].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_AREAS].filelen);
 +	aasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, aasworld.areas);
 +	if (!aasworld.areas) return false;
 +	aasworld.numareas = length / sizeof(aas_area_t);
 +	//area settings
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen);
 +	aasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, aasworld.areasettings);
 +	if (!aasworld.areasettings) return false;
 +	aasworld.numareasettings = length / sizeof(aas_areasettings_t);
 +	//reachability list
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen);
 +	aasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, aasworld.reachability);
 +	if (length && !aasworld.reachability) return false;
 +	aasworld.reachabilitysize = length / sizeof(aas_reachability_t);
 +	//nodes
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_NODES].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_NODES].filelen);
 +	aasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, aasworld.nodes);
 +	if (!aasworld.nodes) return false;
 +	aasworld.numnodes = length / sizeof(aas_node_t);
 +	//cluster portals
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_PORTALS].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_PORTALS].filelen);
 +	aasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, aasworld.portals);
 +	if (length && !aasworld.portals) return false;
 +	aasworld.numportals = length / sizeof(aas_portal_t);
 +	//cluster portal index
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen);
 +	aasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, aasworld.portalindex);
 +	if (length && !aasworld.portalindex) return false;
 +	aasworld.portalindexsize = length / sizeof(aas_portalindex_t);
 +	//clusters
 +	offset = fpoffset + LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs);
 +	length = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen);
 +	aasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, aasworld.clusters);
 +	if (length && !aasworld.clusters) return false;
 +	aasworld.numclusters = length / sizeof(aas_cluster_t);
 +	//swap everything
 +	AAS_SwapAASData();
 +	//aas file is loaded
 +	aasworld.loaded = true;
 +	//close the file
 +	fclose(fp);
 +	return true;
 +} //end of the function AAS_LoadAASFile
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_WriteAASLump(FILE *fp, aas_header_t *h, int lumpnum, void *data, int length)
 +{
 +	aas_lump_t *lump;
 +
 +	lump = &h->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong(ftell(fp));
 +	lump->filelen = LittleLong(length);
 +
 +	if (length > 0)
 +	{
 +		if (fwrite(data, length, 1, fp) < 1)
 +		{
 +			Log_Print("error writing lump %s\n", lumpnum);
 +			fclose(fp);
 +			return false;
 +		} //end if
 +	} //end if
 +	return true;
 +} //end of the function AAS_WriteAASLump
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_ShowNumReachabilities(int tt, char *name)
 +{
 +	int i, num;
 +
 +	num = 0;
 +	for (i = 0; i < aasworld.reachabilitysize; i++)
 +	{
 +		if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == tt)
 +			num++;
 +	} //end for
 +	Log_Print("%6d %s\n", num, name);
 +} //end of the function AAS_ShowNumReachabilities
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_ShowTotals(void)
 +{
 +	Log_Print("numvertexes = %d\r\n", aasworld.numvertexes);
 +	Log_Print("numplanes = %d\r\n", aasworld.numplanes);
 +	Log_Print("numedges = %d\r\n", aasworld.numedges);
 +	Log_Print("edgeindexsize = %d\r\n", aasworld.edgeindexsize);
 +	Log_Print("numfaces = %d\r\n", aasworld.numfaces);
 +	Log_Print("faceindexsize = %d\r\n", aasworld.faceindexsize);
 +	Log_Print("numareas = %d\r\n", aasworld.numareas);
 +	Log_Print("numareasettings = %d\r\n", aasworld.numareasettings);
 +	Log_Print("reachabilitysize = %d\r\n", aasworld.reachabilitysize);
 +	Log_Print("numnodes = %d\r\n", aasworld.numnodes);
 +	Log_Print("numportals = %d\r\n", aasworld.numportals);
 +	Log_Print("portalindexsize = %d\r\n", aasworld.portalindexsize);
 +	Log_Print("numclusters = %d\r\n", aasworld.numclusters);
 +	AAS_ShowNumReachabilities(TRAVEL_WALK, "walk");
 +	AAS_ShowNumReachabilities(TRAVEL_CROUCH, "crouch");
 +	AAS_ShowNumReachabilities(TRAVEL_BARRIERJUMP, "barrier jump");
 +	AAS_ShowNumReachabilities(TRAVEL_JUMP, "jump");
 +	AAS_ShowNumReachabilities(TRAVEL_LADDER, "ladder");
 +	AAS_ShowNumReachabilities(TRAVEL_WALKOFFLEDGE, "walk off ledge");
 +	AAS_ShowNumReachabilities(TRAVEL_SWIM, "swim");
 +	AAS_ShowNumReachabilities(TRAVEL_WATERJUMP, "water jump");
 +	AAS_ShowNumReachabilities(TRAVEL_TELEPORT, "teleport");
 +	AAS_ShowNumReachabilities(TRAVEL_ELEVATOR, "elevator");
 +	AAS_ShowNumReachabilities(TRAVEL_ROCKETJUMP, "rocket jump");
 +	AAS_ShowNumReachabilities(TRAVEL_BFGJUMP, "bfg jump");
 +	AAS_ShowNumReachabilities(TRAVEL_GRAPPLEHOOK, "grapple hook");
 +	AAS_ShowNumReachabilities(TRAVEL_DOUBLEJUMP, "double jump");
 +	AAS_ShowNumReachabilities(TRAVEL_RAMPJUMP, "ramp jump");
 +	AAS_ShowNumReachabilities(TRAVEL_STRAFEJUMP, "strafe jump");
 +	AAS_ShowNumReachabilities(TRAVEL_JUMPPAD, "jump pad");
 +	AAS_ShowNumReachabilities(TRAVEL_FUNCBOB, "func bob");
 +} //end of the function AAS_ShowTotals
 +//===========================================================================
 +// aas data is useless after writing to file because it is byte swapped
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_WriteAASFile(char *filename)
 +{
 +	aas_header_t header;
 +	FILE *fp;
 +
 +	Log_Print("writing %s\n", filename);
 +	AAS_ShowTotals();
 +	//swap the aas data
 +	AAS_SwapAASData();
 +	//initialize the file header
 +	memset(&header, 0, sizeof(aas_header_t));
 +	header.ident = LittleLong(AASID);
 +	header.version = LittleLong(AASVERSION);
 +	header.bspchecksum = LittleLong(aasworld.bspchecksum);
 +	//open a new file
 +	fp = fopen(filename, "wb");
 +	if (!fp)
 +	{
 +		Log_Print("error opening %s\n", filename);
 +		return false;
 +	} //end if
 +	//write the header
 +	if (fwrite(&header, sizeof(aas_header_t), 1, fp) < 1)
 +	{
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//add the data lumps to the file
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes,
 +		aasworld.numbboxes * sizeof(aas_bbox_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes,
 +		aasworld.numvertexes * sizeof(aas_vertex_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes,
 +		aasworld.numplanes * sizeof(aas_plane_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges,
 +		aasworld.numedges * sizeof(aas_edge_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex,
 +		aasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces,
 +		aasworld.numfaces * sizeof(aas_face_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex,
 +		aasworld.faceindexsize * sizeof(aas_faceindex_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas,
 +		aasworld.numareas * sizeof(aas_area_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings,
 +		aasworld.numareasettings * sizeof(aas_areasettings_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability,
 +		aasworld.reachabilitysize * sizeof(aas_reachability_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes,
 +		aasworld.numnodes * sizeof(aas_node_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals,
 +		aasworld.numportals * sizeof(aas_portal_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex,
 +		aasworld.portalindexsize * sizeof(aas_portalindex_t))) return false;
 +	if (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters,
 +		aasworld.numclusters * sizeof(aas_cluster_t))) return false;
 +	//rewrite the header with the added lumps
 +	fseek(fp, 0, SEEK_SET);
 +	AAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);
 +	if (fwrite(&header, sizeof(aas_header_t), 1, fp) < 1)
 +	{
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//close the file
 +	fclose(fp);
 +	return true;
 +} //end of the function AAS_WriteAASFile
 +
 diff --git a/code/bspc/aas_file.h b/code/bspc/aas_file.h new file mode 100755 index 0000000..5176461 --- /dev/null +++ b/code/bspc/aas_file.h @@ -0,0 +1,25 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +qboolean AAS_WriteAASFile(char *filename);
 +qboolean AAS_LoadAASFile(char *filename, int fpoffset, int fplength);
 +
 diff --git a/code/bspc/aas_gsubdiv.c b/code/bspc/aas_gsubdiv.c new file mode 100755 index 0000000..d9ba597 --- /dev/null +++ b/code/bspc/aas_gsubdiv.c @@ -0,0 +1,656 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_create.h"
 +#include "aas_store.h"
 +#include "aas_cfg.h"
 +
 +#define FACECLIP_EPSILON			0.2
 +#define FACE_EPSILON					1.0
 +
 +int numgravitationalsubdivisions = 0;
 +int numladdersubdivisions = 0;
 +
 +//NOTE: only do gravitational subdivision BEFORE area merging!!!!!!!
 +//			because the bsp tree isn't refreshes like with ladder subdivision
 +
 +//===========================================================================
 +// NOTE: the original face is invalid after splitting
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_SplitFace(tmp_face_t *face, vec3_t normal, float dist,
 +							tmp_face_t **frontface, tmp_face_t **backface)
 +{
 +	winding_t *frontw, *backw;
 +
 +	//
 +	*frontface = *backface = NULL;
 +
 +	ClipWindingEpsilon(face->winding, normal, dist, FACECLIP_EPSILON, &frontw, &backw);
 +
 +#ifdef DEBUG
 +	//
 +	if (frontw)
 +	{
 +		if (WindingIsTiny(frontw))
 +		{
 +			Log_Write("AAS_SplitFace: tiny back face\r\n");
 +			FreeWinding(frontw);
 +			frontw = NULL;
 +		} //end if
 +	} //end if
 +	if (backw)
 +	{
 +		if (WindingIsTiny(backw))
 +		{
 +			Log_Write("AAS_SplitFace: tiny back face\r\n");
 +			FreeWinding(backw);
 +			backw = NULL;
 +		} //end if
 +	} //end if
 +#endif //DEBUG
 +	//if the winding was split
 +	if (frontw)
 +	{
 +		//check bounds
 +		(*frontface) = AAS_AllocTmpFace();
 +		(*frontface)->planenum = face->planenum;
 +		(*frontface)->winding = frontw;
 +		(*frontface)->faceflags = face->faceflags;
 +	} //end if
 +	if (backw)
 +	{
 +		//check bounds
 +		(*backface) = AAS_AllocTmpFace();
 +		(*backface)->planenum = face->planenum;
 +		(*backface)->winding = backw;
 +		(*backface)->faceflags = face->faceflags;
 +	} //end if
 +} //end of the function AAS_SplitFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +winding_t *AAS_SplitWinding(tmp_area_t *tmparea, int planenum)
 +{
 +	tmp_face_t *face;
 +	plane_t *plane;
 +	int side;
 +	winding_t *splitwinding;
 +
 +	//
 +	plane = &mapplanes[planenum];
 +	//create a split winding, first base winding for plane
 +	splitwinding = BaseWindingForPlane(plane->normal, plane->dist);
 +	//chop with all the faces of the area
 +	for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side])
 +	{
 +		//side of the face the original area was on
 +		side = face->frontarea != tmparea;
 +		plane = &mapplanes[face->planenum ^ side];
 +		ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON);
 +	} //end for
 +	return splitwinding;
 +} //end of the function AAS_SplitWinding
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_TestSplitPlane(tmp_area_t *tmparea, vec3_t normal, float dist,
 +							int *facesplits, int *groundsplits, int *epsilonfaces)
 +{
 +	int j, side, front, back, planenum;
 +	float d, d_front, d_back;
 +	tmp_face_t *face;
 +	winding_t *w;
 +
 +	*facesplits = *groundsplits = *epsilonfaces = 0;
 +
 +	planenum = FindFloatPlane(normal, dist);
 +
 +	w = AAS_SplitWinding(tmparea, planenum);
 +	if (!w) return false;
 +	FreeWinding(w);
 +	//
 +	for (face = tmparea->tmpfaces; face; face = face->next[side])
 +	{
 +		//side of the face the area is on
 +		side = face->frontarea != tmparea;
 +
 +		if ((face->planenum & ~1) == (planenum & ~1))
 +		{
 +			Log_Print("AAS_TestSplitPlane: tried face plane as splitter\n");
 +			return false;
 +		} //end if
 +		w = face->winding;
 +		//reset distance at front and back side of plane
 +		d_front = d_back = 0;
 +		//reset front and back flags
 +		front = back = 0;
 +		for (j = 0; j < w->numpoints; j++)
 +		{
 +			d = DotProduct(w->p[j], normal) - dist;
 +			if (d > d_front) d_front = d;
 +			if (d < d_back) d_back = d;
 +
 +			if (d > 0.4) // PLANESIDE_EPSILON)
 +				front = 1;
 +			if (d < -0.4) // PLANESIDE_EPSILON)
 +				back = 1;
 +		} //end for
 +		//check for an epsilon face
 +		if ( (d_front > FACECLIP_EPSILON && d_front < FACE_EPSILON)
 +			|| (d_back < -FACECLIP_EPSILON && d_back > -FACE_EPSILON) )
 +		{
 +			(*epsilonfaces)++;
 +		} //end if
 +		//if the face has points at both sides of the plane
 +		if (front && back)
 +		{
 +			(*facesplits)++;
 +			if (face->faceflags & FACE_GROUND)
 +			{
 +				(*groundsplits)++;
 +			} //end if
 +		} //end if
 +	} //end for
 +	return true;
 +} //end of the function AAS_TestSplitPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_SplitArea(tmp_area_t *tmparea, int planenum, tmp_area_t **frontarea, tmp_area_t **backarea)
 +{
 +	int side;
 +	tmp_area_t *facefrontarea, *facebackarea, *faceotherarea;
 +	tmp_face_t *face, *frontface, *backface, *splitface, *nextface;
 +	winding_t *splitwinding;
 +	plane_t *splitplane;
 +
 +/*
 +#ifdef AW_DEBUG
 +	int facesplits, groundsplits, epsilonface;
 +	Log_Print("\n----------------------\n");
 +	Log_Print("splitting area %d\n", areanum);
 +	Log_Print("with normal = \'%f %f %f\', dist = %f\n", normal[0], normal[1], normal[2], dist);
 +	AAS_TestSplitPlane(areanum, normal, dist,
 +										&facesplits, &groundsplits, &epsilonface);
 +	Log_Print("face splits = %d\nground splits = %d\n", facesplits, groundsplits);
 +	if (epsilonface) Log_Print("aaahh epsilon face\n");
 +#endif //AW_DEBUG*/
 +	//the original area
 +
 +	AAS_FlipAreaFaces(tmparea);
 +	AAS_CheckArea(tmparea);
 +	//
 +	splitplane = &mapplanes[planenum];
 +/*	//create a split winding, first base winding for plane
 +	splitwinding = BaseWindingForPlane(splitplane->normal, splitplane->dist);
 +	//chop with all the faces of the area
 +	for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side])
 +	{
 +		//side of the face the original area was on
 +		side = face->frontarea != tmparea->areanum;
 +		plane = &mapplanes[face->planenum ^ side];
 +		ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON);
 +	} //end for*/
 +	splitwinding = AAS_SplitWinding(tmparea, planenum);
 +	if (!splitwinding)
 +	{
 +/*
 +#ifdef DEBUG
 +		AAS_TestSplitPlane(areanum, normal, dist,
 +											&facesplits, &groundsplits, &epsilonface);
 +		Log_Print("\nface splits = %d\nground splits = %d\n", facesplits, groundsplits);
 +		if (epsilonface) Log_Print("aaahh epsilon face\n");
 +#endif //DEBUG*/
 +		Error("AAS_SplitArea: no split winding when splitting area %d\n", tmparea->areanum);
 +	} //end if
 +	//create a split face
 +	splitface = AAS_AllocTmpFace();
 +	//get the map plane
 +	splitface->planenum = planenum;
 +	//store the split winding
 +	splitface->winding = splitwinding;
 +	//the new front area
 +	(*frontarea) = AAS_AllocTmpArea();
 +	(*frontarea)->presencetype = tmparea->presencetype;
 +	(*frontarea)->contents = tmparea->contents;
 +	(*frontarea)->modelnum = tmparea->modelnum;
 +	(*frontarea)->tmpfaces = NULL;
 +	//the new back area
 +	(*backarea) = AAS_AllocTmpArea();
 +	(*backarea)->presencetype = tmparea->presencetype;
 +	(*backarea)->contents = tmparea->contents;
 +	(*backarea)->modelnum = tmparea->modelnum;
 +	(*backarea)->tmpfaces = NULL;
 +	//add the split face to the new areas
 +	AAS_AddFaceSideToArea(splitface, 0, (*frontarea));
 +	AAS_AddFaceSideToArea(splitface, 1, (*backarea));
 +
 +	//split all the faces of the original area
 +	for (face = tmparea->tmpfaces; face; face = nextface)
 +	{
 +		//side of the face the original area was on
 +		side = face->frontarea != tmparea;
 +		//next face of the original area
 +		nextface = face->next[side];
 +		//front area of the face
 +		facefrontarea = face->frontarea;
 +		//back area of the face
 +		facebackarea = face->backarea;
 +		//remove the face from both the front and back areas
 +		if (facefrontarea) AAS_RemoveFaceFromArea(face, facefrontarea);
 +		if (facebackarea) AAS_RemoveFaceFromArea(face, facebackarea);
 +		//split the face
 +		AAS_SplitFace(face, splitplane->normal, splitplane->dist, &frontface, &backface);
 +		//free the original face
 +		AAS_FreeTmpFace(face);
 +		//get the number of the area at the other side of the face
 +		if (side) faceotherarea = facefrontarea;
 +		else faceotherarea = facebackarea;
 +		//if there is an area at the other side of the original face
 +		if (faceotherarea)
 +		{
 +			if (frontface) AAS_AddFaceSideToArea(frontface, !side, faceotherarea);
 +			if (backface) AAS_AddFaceSideToArea(backface, !side, faceotherarea);
 +		} //end if
 +		//add the front and back part left after splitting the original face to the new areas
 +		if (frontface) AAS_AddFaceSideToArea(frontface, side, (*frontarea));
 +		if (backface) AAS_AddFaceSideToArea(backface, side, (*backarea));
 +	} //end for
 +
 +	if (!(*frontarea)->tmpfaces) Log_Print("AAS_SplitArea: front area without faces\n");
 +	if (!(*backarea)->tmpfaces) Log_Print("AAS_SplitArea: back area without faces\n");
 +
 +	tmparea->invalid = true;
 +/*
 +#ifdef AW_DEBUG
 +	for (i = 0, face = frontarea->tmpfaces; face; face = face->next[side])
 +	{
 +		side = face->frontarea != frontarea->areanum;
 +		i++;
 +	} //end for
 +	Log_Print("created front area %d with %d faces\n", frontarea->areanum, i);
 +
 +	for (i = 0, face = backarea->tmpfaces; face; face = face->next[side])
 +	{
 +		side = face->frontarea != backarea->areanum;
 +		i++;
 +	} //end for
 +	Log_Print("created back area %d with %d faces\n", backarea->areanum, i);
 +#endif //AW_DEBUG*/
 +
 +	AAS_FlipAreaFaces((*frontarea));
 +	AAS_FlipAreaFaces((*backarea));
 +	//
 +	AAS_CheckArea((*frontarea));
 +	AAS_CheckArea((*backarea));
 +} //end of the function AAS_SplitArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_FindBestAreaSplitPlane(tmp_area_t *tmparea, vec3_t normal, float *dist)
 +{
 +	int side1, side2;
 +	int foundsplitter, facesplits, groundsplits, epsilonfaces, bestepsilonfaces;
 +	float bestvalue, value;
 +	tmp_face_t *face1, *face2;
 +	vec3_t tmpnormal, invgravity;
 +	float tmpdist;
 +
 +	//get inverse of gravity direction
 +	VectorCopy(cfg.phys_gravitydirection, invgravity);
 +	VectorInverse(invgravity);
 +
 +	foundsplitter = false;
 +	bestvalue = -999999;
 +	bestepsilonfaces = 0;
 +	//
 +#ifdef AW_DEBUG
 +	Log_Print("finding split plane for area %d\n", tmparea->areanum);
 +#endif //AW_DEBUG
 +	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		//side of the face the area is on
 +		side1 = face1->frontarea != tmparea;
 +		//
 +		if (WindingIsTiny(face1->winding))
 +		{
 +			Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum);
 +			continue;
 +		} //end if
 +		//if the face isn't a gap or ground there's no split edge
 +		if (!(face1->faceflags & FACE_GROUND) && !AAS_GapFace(face1, side1)) continue;
 +		//
 +		for (face2 = face1->next[side1]; face2; face2 = face2->next[side2])
 +		{
 +			//side of the face the area is on
 +			side2 = face2->frontarea != tmparea;
 +			//
 +			if (WindingIsTiny(face1->winding))
 +			{
 +				Log_Write("gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum);
 +				continue;
 +			} //end if
 +			//if the face isn't a gap or ground there's no split edge
 +			if (!(face2->faceflags & FACE_GROUND) && !AAS_GapFace(face2, side2)) continue;
 +			//only split between gaps and ground
 +			if (!(((face1->faceflags & FACE_GROUND) && AAS_GapFace(face2, side2)) ||
 +					((face2->faceflags & FACE_GROUND) && AAS_GapFace(face1, side1)))) continue;
 +			//find a plane seperating the windings of the faces
 +			if (!FindPlaneSeperatingWindings(face1->winding, face2->winding, invgravity,
 +														tmpnormal, &tmpdist)) continue;
 +#ifdef AW_DEBUG
 +			Log_Print("normal = \'%f %f %f\', dist = %f\n",
 +							tmpnormal[0], tmpnormal[1], tmpnormal[2], tmpdist);
 +#endif //AW_DEBUG
 +			//get metrics for this vertical plane
 +			if (!AAS_TestSplitPlane(tmparea, tmpnormal, tmpdist,
 +										&facesplits, &groundsplits, &epsilonfaces))
 +			{
 +				continue;
 +			} //end if
 +#ifdef AW_DEBUG
 +			Log_Print("face splits = %d\nground splits = %d\n",
 +							facesplits, groundsplits);
 +#endif //AW_DEBUG
 +			value = 100 - facesplits - 2 * groundsplits;
 +			//avoid epsilon faces
 +			value += epsilonfaces * -1000;
 +			if (value > bestvalue)
 +			{
 +				VectorCopy(tmpnormal, normal);
 +				*dist = tmpdist;
 +				bestvalue = value;
 +				bestepsilonfaces = epsilonfaces;
 +				foundsplitter = true;
 +			} //end if
 +		} //end for
 +	} //end for
 +	if (bestepsilonfaces)
 +	{
 +		Log_Write("found %d epsilon faces trying to split area %d\r\n",
 +									epsilonfaces, tmparea->areanum);
 +	} //end else
 +	return foundsplitter;
 +} //end of the function AAS_FindBestAreaSplitPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_SubdivideArea_r(tmp_node_t *tmpnode)
 +{
 +	int planenum;
 +	tmp_area_t *frontarea, *backarea;
 +	tmp_node_t *tmpnode1, *tmpnode2;
 +	vec3_t normal;
 +	float dist;
 +
 +	if (AAS_FindBestAreaSplitPlane(tmpnode->tmparea, normal, &dist))
 +	{
 +		qprintf("\r%6d", ++numgravitationalsubdivisions);
 +		//
 +		planenum = FindFloatPlane(normal, dist);
 +		//split the area
 +		AAS_SplitArea(tmpnode->tmparea, planenum, &frontarea, &backarea);
 +		//
 +		tmpnode->tmparea = NULL;
 +		tmpnode->planenum = FindFloatPlane(normal, dist);
 +		//
 +		tmpnode1 = AAS_AllocTmpNode();
 +		tmpnode1->planenum = 0;
 +		tmpnode1->tmparea = frontarea;
 +		//
 +		tmpnode2 = AAS_AllocTmpNode();
 +		tmpnode2->planenum = 0;
 +		tmpnode2->tmparea = backarea;
 +		//subdivide the areas created by splitting recursively
 +		tmpnode->children[0] = AAS_SubdivideArea_r(tmpnode1);
 +		tmpnode->children[1] = AAS_SubdivideArea_r(tmpnode2);
 +	} //end if
 +	return tmpnode;
 +} //end of the function AAS_SubdivideArea_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_GravitationalSubdivision_r(tmp_node_t *tmpnode)
 +{
 +	//if this is a solid leaf
 +	if (!tmpnode) return NULL;
 +	//negative so it's an area
 +	if (tmpnode->tmparea) return AAS_SubdivideArea_r(tmpnode);
 +	//do the children recursively
 +	tmpnode->children[0] = AAS_GravitationalSubdivision_r(tmpnode->children[0]);
 +	tmpnode->children[1] = AAS_GravitationalSubdivision_r(tmpnode->children[1]);
 +	return tmpnode;
 +} //end of the function AAS_GravitationalSubdivision_r
 +//===========================================================================
 +// NOTE: merge faces and melt edges first
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_GravitationalSubdivision(void)
 +{
 +	Log_Write("AAS_GravitationalSubdivision\r\n");
 +	numgravitationalsubdivisions = 0;
 +	qprintf("%6i gravitational subdivisions", numgravitationalsubdivisions);
 +	//start with the head node
 +	AAS_GravitationalSubdivision_r(tmpaasworld.nodes);
 +	qprintf("\n");
 +	Log_Write("%6i gravitational subdivisions\r\n", numgravitationalsubdivisions);
 +} //end of the function AAS_GravitationalSubdivision
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_RefreshLadderSubdividedTree_r(tmp_node_t *tmpnode, tmp_area_t *tmparea,
 +												  tmp_node_t *tmpnode1, tmp_node_t *tmpnode2, int planenum)
 +{
 +	//if this is a solid leaf
 +	if (!tmpnode) return NULL;
 +	//negative so it's an area
 +	if (tmpnode->tmparea)
 +	{
 +		if (tmpnode->tmparea == tmparea)
 +		{
 +			tmpnode->tmparea = NULL;
 +			tmpnode->planenum = planenum;
 +			tmpnode->children[0] = tmpnode1;
 +			tmpnode->children[1] = tmpnode2;
 +		} //end if
 +		return tmpnode;
 +	} //end if
 +	//do the children recursively
 +	tmpnode->children[0] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[0],
 +									tmparea, tmpnode1, tmpnode2, planenum);
 +	tmpnode->children[1] = AAS_RefreshLadderSubdividedTree_r(tmpnode->children[1],
 +									tmparea, tmpnode1, tmpnode2, planenum);
 +	return tmpnode;
 +} //end of the function AAS_RefreshLadderSubdividedTree_r
 +//===========================================================================
 +// find an area with ladder faces and ground faces that are not connected
 +// split the area with a horizontal plane at the lowest vertex of all
 +// ladder faces in the area
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_LadderSubdivideArea_r(tmp_node_t *tmpnode)
 +{
 +	int side1, i, planenum;
 +	int foundladderface, foundgroundface;
 +	float dist;
 +	tmp_area_t *tmparea, *frontarea, *backarea;
 +	tmp_face_t *face1;
 +	tmp_node_t *tmpnode1, *tmpnode2;
 +	vec3_t lowestpoint, normal = {0, 0, 1};
 +	plane_t *plane;
 +	winding_t *w;
 +
 +	tmparea = tmpnode->tmparea;
 +	//skip areas with a liquid
 +	if (tmparea->contents & (AREACONTENTS_WATER
 +									| AREACONTENTS_LAVA
 +									| AREACONTENTS_SLIME)) return tmpnode;
 +	//must be possible to stand in the area
 +	if (!(tmparea->presencetype & PRESENCE_NORMAL)) return tmpnode;
 +	//
 +	foundladderface = false;
 +	foundgroundface = false;
 +	lowestpoint[2] = 99999;
 +	//
 +	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		//side of the face the area is on
 +		side1 = face1->frontarea != tmparea;
 +		//if the face is a ladder face
 +		if (face1->faceflags & FACE_LADDER)
 +		{
 +			plane = &mapplanes[face1->planenum];
 +			//the ladder face plane should be pretty much vertical
 +			if (DotProduct(plane->normal, normal) > -0.1)
 +			{
 +				foundladderface = true;
 +				//find lowest point
 +				for (i = 0; i < face1->winding->numpoints; i++)
 +				{
 +					if (face1->winding->p[i][2] < lowestpoint[2])
 +					{
 +						VectorCopy(face1->winding->p[i], lowestpoint);
 +					} //end if
 +				} //end for
 +			} //end if
 +		} //end if
 +		else if (face1->faceflags & FACE_GROUND)
 +		{
 +			foundgroundface = true;
 +		} //end else if
 +	} //end for
 +	//
 +	if ((!foundladderface) || (!foundgroundface)) return tmpnode;
 +	//
 +	for (face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1])
 +	{
 +		//side of the face the area is on
 +		side1 = face1->frontarea != tmparea;
 +		//if the face isn't a ground face
 +		if (!(face1->faceflags & FACE_GROUND)) continue;
 +		//the ground plane
 +		plane = &mapplanes[face1->planenum];
 +		//get the difference between the ground plane and the lowest point
 +		dist = DotProduct(plane->normal, lowestpoint) - plane->dist;
 +		//if the lowest point is very near one of the ground planes
 +		if (dist > -1 && dist < 1)
 +		{
 +			return tmpnode;
 +		} //end if
 +	} //end for
 +	//
 +	dist = DotProduct(normal, lowestpoint);
 +	planenum = FindFloatPlane(normal, dist);
 +	//
 +	w = AAS_SplitWinding(tmparea, planenum);
 +	if (!w) return tmpnode;
 +	FreeWinding(w);
 +	//split the area with a horizontal plane through the lowest point
 +	qprintf("\r%6d", ++numladdersubdivisions);
 +	//
 +	AAS_SplitArea(tmparea, planenum, &frontarea, &backarea);
 +	//
 +	tmpnode->tmparea = NULL;
 +	tmpnode->planenum = planenum;
 +	//
 +	tmpnode1 = AAS_AllocTmpNode();
 +	tmpnode1->planenum = 0;
 +	tmpnode1->tmparea = frontarea;
 +	//
 +	tmpnode2 = AAS_AllocTmpNode();
 +	tmpnode2->planenum = 0;
 +	tmpnode2->tmparea = backarea;
 +	//subdivide the areas created by splitting recursively
 +	tmpnode->children[0] = AAS_LadderSubdivideArea_r(tmpnode1);
 +	tmpnode->children[1] = AAS_LadderSubdivideArea_r(tmpnode2);
 +	//refresh the tree
 +	AAS_RefreshLadderSubdividedTree_r(tmpaasworld.nodes, tmparea, tmpnode1, tmpnode2, planenum);
 +	//
 +	return tmpnode;
 +} //end of the function AAS_LadderSubdivideArea_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_LadderSubdivision_r(tmp_node_t *tmpnode)
 +{
 +	//if this is a solid leaf
 +	if (!tmpnode) return 0;
 +	//negative so it's an area
 +	if (tmpnode->tmparea) return AAS_LadderSubdivideArea_r(tmpnode);
 +	//do the children recursively
 +	tmpnode->children[0] = AAS_LadderSubdivision_r(tmpnode->children[0]);
 +	tmpnode->children[1] = AAS_LadderSubdivision_r(tmpnode->children[1]);
 +	return tmpnode;
 +} //end of the function AAS_LadderSubdivision_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_LadderSubdivision(void)
 +{
 +	Log_Write("AAS_LadderSubdivision\r\n");
 +	numladdersubdivisions = 0;
 +	qprintf("%6i ladder subdivisions", numladdersubdivisions);
 +	//start with the head node
 +	AAS_LadderSubdivision_r(tmpaasworld.nodes);
 +	//
 +	qprintf("\n");
 +	Log_Write("%6i ladder subdivisions\r\n", numladdersubdivisions);
 +} //end of the function AAS_LadderSubdivision
 diff --git a/code/bspc/aas_gsubdiv.h b/code/bspc/aas_gsubdiv.h new file mode 100755 index 0000000..0156a9a --- /dev/null +++ b/code/bspc/aas_gsubdiv.h @@ -0,0 +1,25 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//works with the global tmpaasworld
 +void AAS_GravitationalSubdivision(void);
 +void AAS_LadderSubdivision(void);
 diff --git a/code/bspc/aas_map.c b/code/bspc/aas_map.c new file mode 100755 index 0000000..da16d1b --- /dev/null +++ b/code/bspc/aas_map.c @@ -0,0 +1,849 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"		//aas_bbox_t
 +#include "aas_store.h"				//AAS_MAX_BBOXES
 +#include "aas_cfg.h"
 +#include "../game/surfaceflags.h"
 +
 +#define SPAWNFLAG_NOT_EASY			0x00000100
 +#define SPAWNFLAG_NOT_MEDIUM		0x00000200
 +#define SPAWNFLAG_NOT_HARD			0x00000400
 +#define SPAWNFLAG_NOT_DEATHMATCH	0x00000800
 +#define SPAWNFLAG_NOT_COOP			0x00001000
 +
 +#define STATE_TOP				0
 +#define STATE_BOTTOM			1
 +#define STATE_UP				2
 +#define STATE_DOWN			3
 +
 +#define DOOR_START_OPEN		1
 +#define DOOR_REVERSE			2
 +#define DOOR_CRUSHER			4
 +#define DOOR_NOMONSTER		8
 +#define DOOR_TOGGLE			32
 +#define DOOR_X_AXIS			64
 +#define DOOR_Y_AXIS			128
 +
 +#define BBOX_NORMAL_EPSILON			0.0001
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +vec_t BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side)
 +{
 +	vec3_t v1, v2;
 +	int i;
 +
 +	if (side)
 +	{
 +		for (i = 0; i < 3; i++)
 +		{
 +			if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
 +			else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i];
 +			else v1[i] = 0;
 +		} //end for
 +	} //end if
 +	else
 +	{
 +		for (i = 0; i < 3; i++)
 +		{
 +			if (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i];
 +			else if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i];
 +			else v1[i] = 0;
 +		} //end for
 +	} //end else
 +	VectorCopy(normal, v2);
 +	VectorInverse(v2);
 +	return DotProduct(v1, v2);
 +} //end of the function BoxOriginDistanceFromPlane
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +vec_t CapsuleOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs)
 +{
 +	float offset_up, offset_down, width, radius;
 +
 +	width = maxs[0] - mins[0];
 +	// if the box is less high then it is wide
 +	if (maxs[2] - mins[2] < width) {
 +		width = maxs[2] - mins[2];
 +	}
 +	radius = width * 0.5;
 +	// offset to upper and lower sphere
 +	offset_up = maxs[2] - radius;
 +	offset_down = -mins[2] - radius;
 +
 +	// if normal points upward
 +	if ( normal[2] > 0 ) {
 +		// touches lower sphere first
 +		return normal[2] * offset_down + radius;
 +	}
 +	else {
 +		// touched upper sphere first
 +		return -normal[2] * offset_up + radius;
 +	}
 +}
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_ExpandMapBrush(mapbrush_t *brush, vec3_t mins, vec3_t maxs)
 +{
 +	int sn;
 +	float dist;
 +	side_t *s;
 +	plane_t *plane;
 +
 +	for (sn = 0; sn < brush->numsides; sn++)
 +	{
 +		s = brush->original_sides + sn;
 +		plane = &mapplanes[s->planenum];
 +		dist = plane->dist;
 +		if (capsule_collision) {
 +			dist += CapsuleOriginDistanceFromPlane(plane->normal, mins, maxs);
 +		}
 +		else {
 +			dist += BoxOriginDistanceFromPlane(plane->normal, mins, maxs, 0);
 +		}
 +		s->planenum = FindFloatPlane(plane->normal, dist);
 +		//the side isn't a bevel after expanding
 +		s->flags &= ~SFL_BEVEL;
 +		//don't skip the surface
 +		s->surf &= ~SURF_SKIP;
 +		//make sure the texinfo is not TEXINFO_NODE
 +		//when player clip contents brushes are read from the bsp tree
 +		//they have the texinfo field set to TEXINFO_NODE
 +		//s->texinfo = 0;
 +	} //end for
 +} //end of the function AAS_ExpandMapBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_SetTexinfo(mapbrush_t *brush)
 +{
 +	int n;
 +	side_t *side;
 +
 +	if (brush->contents & (CONTENTS_LADDER
 +									| CONTENTS_AREAPORTAL
 +									| CONTENTS_CLUSTERPORTAL
 +									| CONTENTS_TELEPORTER
 +									| CONTENTS_JUMPPAD
 +									| CONTENTS_DONOTENTER
 +									| CONTENTS_WATER
 +									| CONTENTS_LAVA
 +									| CONTENTS_SLIME
 +									| CONTENTS_WINDOW
 +									| CONTENTS_PLAYERCLIP))
 +	{
 +		//we just set texinfo to 0 because these brush sides MUST be used as
 +		//bsp splitters textured or not textured
 +		for (n = 0; n < brush->numsides; n++)
 +		{
 +			side = brush->original_sides + n;
 +			//side->flags |= SFL_TEXTURED|SFL_VISIBLE;
 +			side->texinfo = 0;
 +		} //end for
 +	} //end if
 +	else
 +	{
 +		//only use brush sides as splitters if they are textured
 +		//texinfo of non-textured sides will be set to TEXINFO_NODE
 +		for (n = 0; n < brush->numsides; n++)
 +		{
 +			side = brush->original_sides + n;
 +			//don't use side as splitter (set texinfo to TEXINFO_NODE) if not textured
 +			if (side->flags & (SFL_TEXTURED|SFL_BEVEL)) side->texinfo = 0;
 +			else side->texinfo = TEXINFO_NODE;
 +		} //end for
 +	} //end else
 +} //end of the function AAS_SetTexinfo
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FreeBrushWindings(mapbrush_t *brush)
 +{
 +	int n;
 +	side_t *side;
 +	//
 +	for (n = 0; n < brush->numsides; n++)
 +	{
 +		side = brush->original_sides + n;
 +		//
 +		if (side->winding) FreeWinding(side->winding);
 +	} //end for
 +} //end of the function FreeBrushWindings
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_AddMapBrushSide(mapbrush_t *brush, int planenum)
 +{
 +	side_t *side;
 +	//
 +	if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +		Error ("MAX_MAPFILE_BRUSHSIDES");
 +	//
 +	side = brush->original_sides + brush->numsides;
 +	side->original = NULL;
 +	side->winding = NULL;
 +	side->contents = brush->contents;
 +	side->flags &= ~(SFL_BEVEL|SFL_VISIBLE);
 +	side->surf = 0;
 +	side->planenum = planenum;
 +	side->texinfo = 0;
 +	//
 +	nummapbrushsides++;
 +	brush->numsides++;
 +} //end of the function AAS_AddMapBrushSide
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FixMapBrush(mapbrush_t *brush)
 +{
 +	int i, j, planenum;
 +	float dist;
 +	winding_t *w;
 +	plane_t *plane, *plane1, *plane2;
 +	side_t *side;
 +	vec3_t normal;
 +
 +	//calculate the brush bounds
 +	ClearBounds(brush->mins, brush->maxs);
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		plane = &mapplanes[brush->original_sides[i].planenum];
 +		w = BaseWindingForPlane(plane->normal, plane->dist);
 +		for (j = 0; j < brush->numsides && w; j++)
 +		{
 +			if (i == j) continue;
 +			//there are no brush bevels marked but who cares :)
 +			if (brush->original_sides[j].flags & SFL_BEVEL) continue;
 +			plane = &mapplanes[brush->original_sides[j].planenum^1];
 +			ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
 +		} //end for
 +
 +		side = &brush->original_sides[i];
 +		side->winding = w;
 +		if (w)
 +		{
 +			for (j = 0; j < w->numpoints; j++)
 +			{
 +				AddPointToBounds(w->p[j], brush->mins, brush->maxs);
 +			} //end for
 +		} //end if
 +	} //end for
 +	//
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		for (j = 0; j < brush->numsides; j++)
 +		{
 +			if (i == j) continue;
 +			plane1 = &mapplanes[brush->original_sides[i].planenum];
 +			plane2 = &mapplanes[brush->original_sides[j].planenum];
 +			if (WindingsNonConvex(brush->original_sides[i].winding,
 +									brush->original_sides[j].winding,
 +									plane1->normal, plane2->normal,
 +									plane1->dist, plane2->dist))
 +			{
 +				Log_Print("non convex brush");
 +			} //end if
 +		} //end for
 +	} //end for
 +
 +	//NOW close the fucking brush!!
 +	for (i = 0; i < 3; i++)
 +	{
 +		if (brush->mins[i] < -MAX_MAP_BOUNDS)
 +		{
 +			VectorClear(normal);
 +			normal[i] = -1;
 +			dist = MAX_MAP_BOUNDS - 10;
 +			planenum = FindFloatPlane(normal, dist);
 +			//
 +			Log_Print("mins out of range: added extra brush side\n");
 +			AAS_AddMapBrushSide(brush, planenum);
 +		} //end if
 +		if (brush->maxs[i] > MAX_MAP_BOUNDS)
 +		{
 +			VectorClear(normal);
 +			normal[i] = 1;
 +			dist = MAX_MAP_BOUNDS - 10;
 +			planenum = FindFloatPlane(normal, dist);
 +			//
 +			Log_Print("maxs out of range: added extra brush side\n");
 +			AAS_AddMapBrushSide(brush, planenum);
 +		} //end if
 +		if (brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum);
 +		} //end if
 +	} //end for
 +	//free all the windings
 +	FreeBrushWindings(brush);
 +} //end of the function AAS_FixMapBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_MakeBrushWindings(mapbrush_t *ob)
 +{
 +	int			i, j;
 +	winding_t	*w;
 +	side_t		*side;
 +	plane_t		*plane, *plane1, *plane2;
 +
 +	ClearBounds (ob->mins, ob->maxs);
 +
 +	for (i = 0; i < ob->numsides; i++)
 +	{
 +		plane = &mapplanes[ob->original_sides[i].planenum];
 +		w = BaseWindingForPlane(plane->normal, plane->dist);
 +		for (j = 0; j <ob->numsides && w; j++)
 +		{
 +			if (i == j) continue;
 +			if (ob->original_sides[j].flags & SFL_BEVEL) continue;
 +			plane = &mapplanes[ob->original_sides[j].planenum^1];
 +			ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
 +		}
 +
 +		side = &ob->original_sides[i];
 +		side->winding = w;
 +		if (w)
 +		{
 +			side->flags |= SFL_VISIBLE;
 +			for (j = 0; j < w->numpoints; j++)
 +				AddPointToBounds (w->p[j], ob->mins, ob->maxs);
 +		}
 +	}
 +	//check if the brush is convex
 +	for (i = 0; i < ob->numsides; i++)
 +	{
 +		for (j = 0; j < ob->numsides; j++)
 +		{
 +			if (i == j) continue;
 +			plane1 = &mapplanes[ob->original_sides[i].planenum];
 +			plane2 = &mapplanes[ob->original_sides[j].planenum];
 +			if (WindingsNonConvex(ob->original_sides[i].winding,
 +									ob->original_sides[j].winding,
 +									plane1->normal, plane2->normal,
 +									plane1->dist, plane2->dist))
 +			{
 +				Log_Print("non convex brush");
 +			} //end if
 +		} //end for
 +	} //end for
 +	//check for out of bound brushes
 +	for (i = 0; i < 3; i++)
 +	{
 +		//IDBUG: all the indexes into the mins and maxs were zero (not using i)
 +		if (ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);
 +			Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i]);
 +			ob->numsides = 0; //remove the brush
 +			break;
 +		} //end if
 +		if (ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);
 +			Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i]);
 +			ob->numsides = 0; //remove the brush
 +			break;
 +		} //end if
 +	} //end for
 +	return true;
 +} //end of the function AAS_MakeBrushWindings
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +mapbrush_t *AAS_CopyMapBrush(mapbrush_t *brush, entity_t *mapent)
 +{
 +	int n;
 +	mapbrush_t *newbrush;
 +	side_t *side, *newside;
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +		Error ("MAX_MAPFILE_BRUSHES");
 +
 +	newbrush = &mapbrushes[nummapbrushes];
 +	newbrush->original_sides = &brushsides[nummapbrushsides];
 +	newbrush->entitynum = brush->entitynum;
 +	newbrush->brushnum = nummapbrushes - mapent->firstbrush;
 +	newbrush->numsides = brush->numsides;
 +	newbrush->contents = brush->contents;
 +
 +	//copy the sides
 +	for (n = 0; n < brush->numsides; n++)
 +	{
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		side = brush->original_sides + n;
 +
 +		newside = newbrush->original_sides + n;
 +		newside->original = NULL;
 +		newside->winding = NULL;
 +		newside->contents = side->contents;
 +		newside->flags = side->flags;
 +		newside->surf = side->surf;
 +		newside->planenum = side->planenum;
 +		newside->texinfo = side->texinfo;
 +		nummapbrushsides++;
 +	} //end for
 +	//
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +	return newbrush;
 +} //end of the function AAS_CopyMapBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int mark_entities[MAX_MAP_ENTITIES];
 +
 +int AAS_AlwaysTriggered_r(char *targetname)
 +{
 +	int i;
 +
 +	if (!strlen(targetname)) {
 +		return false;
 +	}
 +	//
 +	for (i = 0; i < num_entities; i++) {
 +		// if the entity will activate the given targetname
 +		if ( !strcmp(targetname, ValueForKey(&entities[i], "target")) ) {
 +			// if this activator is present in deathmatch
 +			if (!(atoi(ValueForKey(&entities[i], "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH)) {
 +				// if it is a trigger_always entity
 +				if (!strcmp("trigger_always", ValueForKey(&entities[i], "classname"))) {
 +					return true;
 +				}
 +				// check for possible trigger_always entities activating this entity
 +				if ( mark_entities[i] ) {
 +					Warning( "entity %d, classname %s has recursive targetname %s\n", i,
 +										ValueForKey(&entities[i], "classname"), targetname );
 +					return false;
 +				}
 +				mark_entities[i] = true;
 +				if ( AAS_AlwaysTriggered_r(ValueForKey(&entities[i], "targetname")) ) {
 +					return true;
 +				}
 +			}
 +		}
 +	}
 +	return false;
 +}
 +
 +int AAS_AlwaysTriggered(char *targetname) {
 +	memset( mark_entities, 0, sizeof(mark_entities) );
 +	return AAS_AlwaysTriggered_r( targetname );
 +}
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_ValidEntity(entity_t *mapent)
 +{
 +	int i;
 +	char target[1024];
 +
 +	//all world brushes are used for AAS
 +	if (mapent == &entities[0])
 +	{
 +		return true;
 +	} //end if
 +	//some of the func_wall brushes are also used for AAS
 +	else if (!strcmp("func_wall", ValueForKey(mapent, "classname")))
 +	{
 +		//Log_Print("found func_wall entity %d\n", mapent - entities);
 +		//if the func wall is used in deathmatch
 +		if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH))
 +		{
 +			//Log_Print("func_wall USED in deathmatch mode %d\n", atoi(ValueForKey(mapent, "spawnflags")));
 +			return true;
 +		} //end if
 +	} //end else if
 +	else if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname")))
 +	{
 +		//if the func_door_rotating is present in deathmatch
 +		if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH))
 +		{
 +			//if the func_door_rotating is always activated in deathmatch
 +			if (AAS_AlwaysTriggered(ValueForKey(mapent, "targetname")))
 +			{
 +				//Log_Print("found func_door_rotating in deathmatch\ntargetname %s\n", ValueForKey(mapent, "targetname"));
 +				return true;
 +			} //end if
 +		} //end if
 +	} //end else if
 +	else if (!strcmp("trigger_hurt", ValueForKey(mapent, "classname")))
 +	{
 +		//"dmg" is the damage, for instance: "dmg" "666"
 +		return true;
 +	} //end else if
 +	else if (!strcmp("trigger_push", ValueForKey(mapent, "classname")))
 +	{
 +		return true;
 +	} //end else if
 +	else if (!strcmp("trigger_multiple", ValueForKey(mapent, "classname")))
 +	{
 +		//find out if the trigger_multiple is pointing to a target_teleporter
 +		strcpy(target, ValueForKey(mapent, "target"));
 +		for (i = 0; i < num_entities; i++)
 +		{
 +			//if the entity will activate the given targetname
 +			if (!strcmp(target, ValueForKey(&entities[i], "targetname")))
 +			{
 +				if (!strcmp("target_teleporter", ValueForKey(&entities[i], "classname")))
 +				{
 +					return true;
 +				} //end if
 +			} //end if
 +		} //end for
 +	} //end else if
 +	else if (!strcmp("trigger_teleport", ValueForKey(mapent, "classname")))
 +	{
 +		return true;
 +	} //end else if
 +	else if (!strcmp("func_static", ValueForKey(mapent, "classname")))
 +	{
 +		//FIXME: easy/medium/hard/deathmatch specific?
 +		return true;
 +	} //end else if
 +	else if (!strcmp("func_door", ValueForKey(mapent, "classname")))
 +	{
 +		return true;
 +	} //end else if
 +	return false;
 +} //end of the function AAS_ValidEntity
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_TransformPlane(int planenum, vec3_t origin, vec3_t angles)
 +{
 +	float newdist, matrix[3][3];
 +	vec3_t normal;
 +
 +	//rotate the node plane
 +	VectorCopy(mapplanes[planenum].normal, normal);
 +	CreateRotationMatrix(angles, matrix);
 +	RotatePoint(normal, matrix);
 +	newdist = mapplanes[planenum].dist + DotProduct(normal, origin);
 +	return FindFloatPlane(normal, newdist);
 +} //end of the function AAS_TransformPlane
 +//===========================================================================
 +// this function sets the func_rotating_door in it's final position
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_PositionFuncRotatingBrush(entity_t *mapent, mapbrush_t *brush)
 +{
 +	int spawnflags, i;
 +	float distance;
 +	vec3_t movedir, angles, pos1, pos2;
 +	side_t *s;
 +
 +	spawnflags = FloatForKey(mapent, "spawnflags");
 +	VectorClear(movedir);
 +	if (spawnflags & DOOR_X_AXIS)
 +		movedir[2] = 1.0;		//roll
 +	else if (spawnflags & DOOR_Y_AXIS)
 +		movedir[0] = 1.0;		//pitch
 +	else // Z_AXIS
 +		movedir[1] = 1.0;		//yaw
 +
 +	// check for reverse rotation
 +	if (spawnflags & DOOR_REVERSE)
 +		VectorInverse(movedir);
 +
 +	distance = FloatForKey(mapent, "distance");
 +	if (!distance) distance = 90;
 +
 +	GetVectorForKey(mapent, "angles", angles);
 +	VectorCopy(angles, pos1);
 +	VectorMA(angles, -distance, movedir, pos2);
 +	// if it starts open, switch the positions
 +	if (spawnflags & DOOR_START_OPEN)
 +	{
 +		VectorCopy(pos2, angles);
 +		VectorCopy(pos1, pos2);
 +		VectorCopy(angles, pos1);
 +		VectorInverse(movedir);
 +	} //end if
 +	//
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		s = &brush->original_sides[i];
 +		s->planenum = AAS_TransformPlane(s->planenum, mapent->origin, pos2);
 +	} //end for
 +	//
 +	FreeBrushWindings(brush);
 +	AAS_MakeBrushWindings(brush);
 +	AddBrushBevels(brush);
 +	FreeBrushWindings(brush);
 +} //end of the function AAS_PositionFuncRotatingBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_PositionBrush(entity_t *mapent, mapbrush_t *brush)
 +{
 +	side_t *s;
 +	float newdist;
 +	int i, notteam;
 +	char *model;
 +
 +	if (!strcmp(ValueForKey(mapent, "classname"), "func_door_rotating"))
 +	{
 +		AAS_PositionFuncRotatingBrush(mapent, brush);
 +	} //end if
 +	else
 +	{
 +		if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
 +		{
 +			for (i = 0; i < brush->numsides; i++)
 +			{
 +				s = &brush->original_sides[i];
 +				newdist = mapplanes[s->planenum].dist +
 +						DotProduct(mapplanes[s->planenum].normal, mapent->origin);
 +				s->planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist);
 +			} //end for
 +		} //end if
 +		//if it's a trigger hurt
 +		if (!strcmp("trigger_hurt", ValueForKey(mapent, "classname")))
 +		{
 +			notteam = FloatForKey(mapent, "bot_notteam");
 +			if ( notteam == 1 ) {
 +				brush->contents |= CONTENTS_NOTTEAM1;
 +			}
 +			else if ( notteam == 2 ) {
 +				brush->contents |= CONTENTS_NOTTEAM2;
 +			}
 +			else {
 +				// always avoid so set lava contents
 +				brush->contents |= CONTENTS_LAVA;
 +			}
 +		} //end if
 +		//
 +		else if (!strcmp("trigger_push", ValueForKey(mapent, "classname")))
 +		{
 +			//set the jumppad contents
 +			brush->contents = CONTENTS_JUMPPAD;
 +			//Log_Print("found trigger_push brush\n");
 +		} //end if
 +		//
 +		else if (!strcmp("trigger_multiple", ValueForKey(mapent, "classname")))
 +		{
 +			//set teleporter contents
 +			brush->contents = CONTENTS_TELEPORTER;
 +			//Log_Print("found trigger_multiple teleporter brush\n");
 +		} //end if
 +		//
 +		else if (!strcmp("trigger_teleport", ValueForKey(mapent, "classname")))
 +		{
 +			//set teleporter contents
 +			brush->contents = CONTENTS_TELEPORTER;
 +			//Log_Print("found trigger_teleport teleporter brush\n");
 +		} //end if
 +		else if (!strcmp("func_door", ValueForKey(mapent, "classname")))
 +		{
 +			//set mover contents
 +			brush->contents = CONTENTS_MOVER;
 +			//get the model number
 +			model = ValueForKey(mapent, "model");
 +			brush->modelnum = atoi(model+1);
 +		} //end if
 +	} //end else
 +} //end of the function AAS_PositionBrush
 +//===========================================================================
 +// uses the global cfg_t cfg
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CreateMapBrushes(mapbrush_t *brush, entity_t *mapent, int addbevels)
 +{
 +	int i;
 +	//side_t *s;
 +	mapbrush_t *bboxbrushes[16];
 +
 +	//if the brushes are not from an entity used for AAS
 +	if (!AAS_ValidEntity(mapent))
 +	{
 +		nummapbrushsides -= brush->numsides;
 +		brush->numsides = 0;
 +		return;
 +	} //end if
 +	//
 +	AAS_PositionBrush(mapent, brush);
 +	//from all normal solid brushes only the textured brush sides will
 +	//be used as bsp splitters, so set the right texinfo reference here
 +	AAS_SetTexinfo(brush);
 +	//remove contents detail flag, otherwise player clip contents won't be
 +	//bsped correctly for AAS!
 +	brush->contents &= ~CONTENTS_DETAIL;
 +	//if the brush has contents area portal it should be the only contents
 +	if (brush->contents & (CONTENTS_AREAPORTAL|CONTENTS_CLUSTERPORTAL))
 +	{
 +		brush->contents = CONTENTS_CLUSTERPORTAL;
 +		brush->leafnum = -1;
 +	} //end if
 +	//window and playerclip are used for player clipping, make them solid
 +	if (brush->contents & (CONTENTS_WINDOW | CONTENTS_PLAYERCLIP))
 +	{
 +		//
 +		brush->contents &= ~(CONTENTS_WINDOW | CONTENTS_PLAYERCLIP);
 +		brush->contents |= CONTENTS_SOLID;
 +		brush->leafnum = -1;
 +	} //end if
 +	//
 +	if (brush->contents & CONTENTS_BOTCLIP)
 +	{
 +		brush->contents = CONTENTS_SOLID;
 +		brush->leafnum = -1;
 +	} //end if
 +	//
 +	//Log_Write("brush %d contents = ", brush->brushnum);
 +	//PrintContents(brush->contents);
 +	//Log_Write("\r\n");
 +	//if not one of the following brushes then the brush is NOT used for AAS
 +	if (!(brush->contents & (CONTENTS_SOLID
 +									| CONTENTS_LADDER
 +									| CONTENTS_CLUSTERPORTAL
 +									| CONTENTS_DONOTENTER
 +									| CONTENTS_TELEPORTER
 +									| CONTENTS_JUMPPAD
 +									| CONTENTS_WATER
 +									| CONTENTS_LAVA
 +									| CONTENTS_SLIME
 +									| CONTENTS_MOVER
 +									)))
 +	{
 +		nummapbrushsides -= brush->numsides;
 +		brush->numsides = 0;
 +		return;
 +	} //end if
 +	//fix the map brush
 +	//AAS_FixMapBrush(brush);
 +	//if brush bevels should be added (for real map brushes, not bsp map brushes)
 +	if (addbevels)
 +	{
 +		//NOTE: we first have to get the mins and maxs of the brush before
 +		//			creating the brush bevels... the mins and maxs are used to
 +		//			create them. so we call MakeBrushWindings to get the mins
 +		//			and maxs and then after creating the bevels we free the
 +		//			windings because they are created for all sides (including
 +		//			bevels) a little later
 +		AAS_MakeBrushWindings(brush);
 +		AddBrushBevels(brush);
 +		FreeBrushWindings(brush);
 +	} //end if
 +	//NOTE: add the brush to the WORLD entity!!!
 +	mapent = &entities[0];
 +	//there's at least one new brush for now
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +	//liquid brushes are expanded for the maximum possible bounding box
 +	if (brush->contents & (CONTENTS_WATER
 +									| CONTENTS_LAVA
 +									| CONTENTS_SLIME 
 +									| CONTENTS_TELEPORTER
 +									| CONTENTS_JUMPPAD
 +									| CONTENTS_DONOTENTER
 +									| CONTENTS_MOVER
 +									))
 +	{
 +		brush->expansionbbox = 0;
 +		//NOTE: the first bounding box is the max
 +		//FIXME: use max bounding box created from all bboxes
 +		AAS_ExpandMapBrush(brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs);
 +		AAS_MakeBrushWindings(brush);
 +	} //end if
 +	//area portal brushes are NOT expanded
 +	else if (brush->contents & CONTENTS_CLUSTERPORTAL)
 +	{
 +		brush->expansionbbox = 0;
 +		//NOTE: the first bounding box is the max
 +		//FIXME: use max bounding box created from all bboxes
 +		AAS_ExpandMapBrush(brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs);
 +		AAS_MakeBrushWindings(brush);
 +	} //end if
 +	//all solid brushes are expanded for all bounding boxes
 +	else if (brush->contents & (CONTENTS_SOLID
 +										| CONTENTS_LADDER
 +										))
 +	{
 +		//brush for the first bounding box
 +		bboxbrushes[0] = brush;
 +		//make a copy for the other bounding boxes
 +		for (i = 1; i < cfg.numbboxes; i++)
 +		{
 +			bboxbrushes[i] = AAS_CopyMapBrush(brush, mapent);
 +		} //end for
 +		//expand every brush for it's bounding box and create windings
 +		for (i = 0; i < cfg.numbboxes; i++)
 +		{
 +			AAS_ExpandMapBrush(bboxbrushes[i], cfg.bboxes[i].mins, cfg.bboxes[i].maxs);
 +			bboxbrushes[i]->expansionbbox = cfg.bboxes[i].presencetype;
 +			AAS_MakeBrushWindings(bboxbrushes[i]);
 +		} //end for
 +	} //end else
 +} //end of the function AAS_CreateMapBrushes
 diff --git a/code/bspc/aas_map.h b/code/bspc/aas_map.h new file mode 100755 index 0000000..601cdfb --- /dev/null +++ b/code/bspc/aas_map.h @@ -0,0 +1,23 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void AAS_CreateMapBrushes(mapbrush_t *brush, entity_t *mapent, int addbevels);
 diff --git a/code/bspc/aas_prunenodes.c b/code/bspc/aas_prunenodes.c new file mode 100755 index 0000000..a03e9d2 --- /dev/null +++ b/code/bspc/aas_prunenodes.c @@ -0,0 +1,89 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_create.h"
 +
 +int c_numprunes;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tmp_node_t *AAS_PruneNodes_r(tmp_node_t *tmpnode)
 +{
 +	tmp_area_t *tmparea1, *tmparea2;
 +
 +	//if it is a solid leaf
 +	if (!tmpnode) return NULL;
 +	//
 +	if (tmpnode->tmparea) return tmpnode;
 +	//process the children first
 +	tmpnode->children[0] = AAS_PruneNodes_r(tmpnode->children[0]);
 +	tmpnode->children[1] = AAS_PruneNodes_r(tmpnode->children[1]);
 +	//if both children are areas
 +	if (tmpnode->children[0] && tmpnode->children[1] &&
 +			tmpnode->children[0]->tmparea && tmpnode->children[1]->tmparea)
 +	{
 +		tmparea1 = tmpnode->children[0]->tmparea;
 +		while(tmparea1->mergedarea) tmparea1 = tmparea1->mergedarea;
 +
 +		tmparea2 = tmpnode->children[1]->tmparea;
 +		while(tmparea2->mergedarea) tmparea2 = tmparea2->mergedarea;
 +
 +		if (tmparea1 == tmparea2)
 +		{
 +			c_numprunes++;
 +			tmpnode->tmparea = tmparea1;
 +			tmpnode->planenum = 0;
 +			AAS_FreeTmpNode(tmpnode->children[0]);
 +			AAS_FreeTmpNode(tmpnode->children[1]);
 +			tmpnode->children[0] = NULL;
 +			tmpnode->children[1] = NULL;
 +			return tmpnode;
 +		} //end if
 +	} //end if
 +	//if both solid leafs
 +	if (!tmpnode->children[0] && !tmpnode->children[1])
 +	{
 +		c_numprunes++;
 +		AAS_FreeTmpNode(tmpnode);
 +		return NULL;
 +	} //end if
 +	//
 +	return tmpnode;
 +} //end of the function AAS_PruneNodes_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_PruneNodes(void)
 +{
 +	Log_Write("AAS_PruneNodes\r\n");
 +	AAS_PruneNodes_r(tmpaasworld.nodes);
 +	Log_Print("%6d nodes pruned\r\n", c_numprunes);
 +} //end of the function AAS_PruneNodes
 diff --git a/code/bspc/aas_prunenodes.h b/code/bspc/aas_prunenodes.h new file mode 100755 index 0000000..36989ad --- /dev/null +++ b/code/bspc/aas_prunenodes.h @@ -0,0 +1,24 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void AAS_PruneNodes(void);
 +
 diff --git a/code/bspc/aas_store.c b/code/bspc/aas_store.c new file mode 100755 index 0000000..4836d51 --- /dev/null +++ b/code/bspc/aas_store.c @@ -0,0 +1,1082 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_file.h"
 +#include "aas_store.h"
 +#include "aas_create.h"
 +#include "aas_cfg.h"
 +
 +
 +//#define NOTHREEVERTEXFACES
 +
 +#define STOREPLANESDOUBLE
 +
 +#define VERTEX_EPSILON			0.1			//NOTE: changed from 0.5
 +#define DIST_EPSILON			0.05		//NOTE: changed from 0.9
 +#define NORMAL_EPSILON			0.0001		//NOTE: changed from 0.005
 +#define INTEGRAL_EPSILON		0.01
 +
 +#define VERTEX_HASHING
 +#define VERTEX_HASH_SHIFT		7
 +#define VERTEX_HASH_SIZE		((MAX_MAP_BOUNDS>>(VERTEX_HASH_SHIFT-1))+1)	//was 64
 +//
 +#define PLANE_HASHING
 +#define PLANE_HASH_SIZE			1024		//must be power of 2
 +//
 +#define EDGE_HASHING
 +#define EDGE_HASH_SIZE			1024		//must be power of 2
 +
 +aas_t aasworld;
 +
 +//vertex hash
 +int *aas_vertexchain;						// the next vertex in a hash chain
 +int aas_hashverts[VERTEX_HASH_SIZE*VERTEX_HASH_SIZE];	// a vertex number, or 0 for no verts
 +//plane hash
 +int *aas_planechain;
 +int aas_hashplanes[PLANE_HASH_SIZE];
 +//edge hash
 +int *aas_edgechain;
 +int aas_hashedges[EDGE_HASH_SIZE];
 +
 +int allocatedaasmem = 0;
 +
 +int groundfacesonly = false;//true;
 +//
 +typedef struct max_aas_s
 +{
 +	int max_bboxes;
 +	int max_vertexes;
 +	int max_planes;
 +	int max_edges;
 +	int max_edgeindexsize;
 +	int max_faces;
 +	int max_faceindexsize;
 +	int max_areas;
 +	int max_areasettings;
 +	int max_reachabilitysize;
 +	int max_nodes;
 +	int max_portals;
 +	int max_portalindexsize;
 +	int max_clusters;
 +} max_aas_t;
 +//maximums of everything
 +max_aas_t max_aas;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_CountTmpNodes(tmp_node_t *tmpnode)
 +{
 +	if (!tmpnode) return 0;
 +	return AAS_CountTmpNodes(tmpnode->children[0]) +
 +				AAS_CountTmpNodes(tmpnode->children[1]) + 1;
 +} //end of the function AAS_CountTmpNodes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_InitMaxAAS(void)
 +{
 +	int numfaces, numpoints, numareas;
 +	tmp_face_t *f;
 +	tmp_area_t *a;
 +
 +	numpoints = 0;
 +	numfaces = 0;
 +	for (f = tmpaasworld.faces; f; f = f->l_next)
 +	{
 +		numfaces++;
 +		if (f->winding) numpoints += f->winding->numpoints;
 +	} //end for
 +	//
 +	numareas = 0;
 +	for (a = tmpaasworld.areas; a; a = a->l_next)
 +	{
 +		numareas++;
 +	} //end for
 +	max_aas.max_bboxes = AAS_MAX_BBOXES;
 +	max_aas.max_vertexes = numpoints + 1;
 +	max_aas.max_planes = nummapplanes;
 +	max_aas.max_edges = numpoints + 1;
 +	max_aas.max_edgeindexsize = (numpoints + 1) * 3;
 +	max_aas.max_faces = numfaces + 10;
 +	max_aas.max_faceindexsize = (numfaces + 10) * 2;
 +	max_aas.max_areas = numareas + 10;
 +	max_aas.max_areasettings = numareas + 10;
 +	max_aas.max_reachabilitysize = 0;
 +	max_aas.max_nodes = AAS_CountTmpNodes(tmpaasworld.nodes) + 10;
 +	max_aas.max_portals = 0;
 +	max_aas.max_portalindexsize = 0;
 +	max_aas.max_clusters = 0;
 +} //end of the function AAS_InitMaxAAS
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_AllocMaxAAS(void)
 +{
 +	int i;
 +
 +	AAS_InitMaxAAS();
 +	//bounding boxes
 +	aasworld.numbboxes = 0;
 +	aasworld.bboxes = (aas_bbox_t *) GetClearedMemory(max_aas.max_bboxes * sizeof(aas_bbox_t));
 +	allocatedaasmem += max_aas.max_bboxes * sizeof(aas_bbox_t);
 +	//vertexes
 +	aasworld.numvertexes = 0;
 +	aasworld.vertexes = (aas_vertex_t *) GetClearedMemory(max_aas.max_vertexes * sizeof(aas_vertex_t));
 +	allocatedaasmem += max_aas.max_vertexes * sizeof(aas_vertex_t);
 +	//planes
 +	aasworld.numplanes = 0;
 +	aasworld.planes = (aas_plane_t *) GetClearedMemory(max_aas.max_planes * sizeof(aas_plane_t));
 +	allocatedaasmem += max_aas.max_planes * sizeof(aas_plane_t);
 +	//edges
 +	aasworld.numedges = 0;
 +	aasworld.edges = (aas_edge_t *) GetClearedMemory(max_aas.max_edges * sizeof(aas_edge_t));
 +	allocatedaasmem += max_aas.max_edges * sizeof(aas_edge_t);
 +	//edge index
 +	aasworld.edgeindexsize = 0;
 +	aasworld.edgeindex = (aas_edgeindex_t *) GetClearedMemory(max_aas.max_edgeindexsize * sizeof(aas_edgeindex_t));
 +	allocatedaasmem += max_aas.max_edgeindexsize * sizeof(aas_edgeindex_t);
 +	//faces
 +	aasworld.numfaces = 0;
 +	aasworld.faces = (aas_face_t *) GetClearedMemory(max_aas.max_faces * sizeof(aas_face_t));
 +	allocatedaasmem += max_aas.max_faces * sizeof(aas_face_t);
 +	//face index
 +	aasworld.faceindexsize = 0;
 +	aasworld.faceindex = (aas_faceindex_t *) GetClearedMemory(max_aas.max_faceindexsize * sizeof(aas_faceindex_t));
 +	allocatedaasmem += max_aas.max_faceindexsize * sizeof(aas_faceindex_t);
 +	//convex areas
 +	aasworld.numareas = 0;
 +	aasworld.areas = (aas_area_t *) GetClearedMemory(max_aas.max_areas * sizeof(aas_area_t));
 +	allocatedaasmem += max_aas.max_areas * sizeof(aas_area_t);
 +	//convex area settings
 +	aasworld.numareasettings = 0;
 +	aasworld.areasettings = (aas_areasettings_t *) GetClearedMemory(max_aas.max_areasettings * sizeof(aas_areasettings_t));
 +	allocatedaasmem += max_aas.max_areasettings * sizeof(aas_areasettings_t);
 +	//reachablity list
 +	aasworld.reachabilitysize = 0;
 +	aasworld.reachability = (aas_reachability_t *) GetClearedMemory(max_aas.max_reachabilitysize * sizeof(aas_reachability_t));
 +	allocatedaasmem += max_aas.max_reachabilitysize * sizeof(aas_reachability_t);
 +	//nodes of the bsp tree
 +	aasworld.numnodes = 0;
 +	aasworld.nodes = (aas_node_t *) GetClearedMemory(max_aas.max_nodes * sizeof(aas_node_t));
 +	allocatedaasmem += max_aas.max_nodes * sizeof(aas_node_t);
 +	//cluster portals
 +	aasworld.numportals = 0;
 +	aasworld.portals = (aas_portal_t *) GetClearedMemory(max_aas.max_portals * sizeof(aas_portal_t));
 +	allocatedaasmem += max_aas.max_portals * sizeof(aas_portal_t);
 +	//cluster portal index
 +	aasworld.portalindexsize = 0;
 +	aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(max_aas.max_portalindexsize * sizeof(aas_portalindex_t));
 +	allocatedaasmem += max_aas.max_portalindexsize * sizeof(aas_portalindex_t);
 +	//cluster
 +	aasworld.numclusters = 0;
 +	aasworld.clusters = (aas_cluster_t *) GetClearedMemory(max_aas.max_clusters * sizeof(aas_cluster_t));
 +	allocatedaasmem += max_aas.max_clusters * sizeof(aas_cluster_t);
 +	//
 +	Log_Print("allocated ");
 +	PrintMemorySize(allocatedaasmem);
 +	Log_Print(" of AAS memory\n");
 +	//reset the has stuff
 +	aas_vertexchain = (int *) GetClearedMemory(max_aas.max_vertexes * sizeof(int));
 +	aas_planechain = (int *) GetClearedMemory(max_aas.max_planes * sizeof(int));
 +	aas_edgechain = (int *) GetClearedMemory(max_aas.max_edges * sizeof(int));
 +	//
 +	for (i = 0; i < max_aas.max_vertexes; i++) aas_vertexchain[i] = -1;
 +	for (i = 0; i < VERTEX_HASH_SIZE * VERTEX_HASH_SIZE; i++) aas_hashverts[i] = -1;
 +	//
 +	for (i = 0; i < max_aas.max_planes; i++) aas_planechain[i] = -1;
 +	for (i = 0; i < PLANE_HASH_SIZE; i++) aas_hashplanes[i] = -1;
 +	//
 +	for (i = 0; i < max_aas.max_edges; i++) aas_edgechain[i] = -1;
 +	for (i = 0; i < EDGE_HASH_SIZE; i++) aas_hashedges[i] = -1;
 +} //end of the function AAS_AllocMaxAAS
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_FreeMaxAAS(void)
 +{
 +	//bounding boxes
 +	if (aasworld.bboxes) FreeMemory(aasworld.bboxes);
 +	aasworld.bboxes = NULL;
 +	aasworld.numbboxes = 0;
 +	//vertexes
 +	if (aasworld.vertexes) FreeMemory(aasworld.vertexes);
 +	aasworld.vertexes = NULL;
 +	aasworld.numvertexes = 0;
 +	//planes
 +	if (aasworld.planes) FreeMemory(aasworld.planes);
 +	aasworld.planes = NULL;
 +	aasworld.numplanes = 0;
 +	//edges
 +	if (aasworld.edges) FreeMemory(aasworld.edges);
 +	aasworld.edges = NULL;
 +	aasworld.numedges = 0;
 +	//edge index
 +	if (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);
 +	aasworld.edgeindex = NULL;
 +	aasworld.edgeindexsize = 0;
 +	//faces
 +	if (aasworld.faces) FreeMemory(aasworld.faces);
 +	aasworld.faces = NULL;
 +	aasworld.numfaces = 0;
 +	//face index
 +	if (aasworld.faceindex) FreeMemory(aasworld.faceindex);
 +	aasworld.faceindex = NULL;
 +	aasworld.faceindexsize = 0;
 +	//convex areas
 +	if (aasworld.areas) FreeMemory(aasworld.areas);
 +	aasworld.areas = NULL;
 +	aasworld.numareas = 0;
 +	//convex area settings
 +	if (aasworld.areasettings) FreeMemory(aasworld.areasettings);
 +	aasworld.areasettings = NULL;
 +	aasworld.numareasettings = 0;
 +	//reachablity list
 +	if (aasworld.reachability) FreeMemory(aasworld.reachability);
 +	aasworld.reachability = NULL;
 +	aasworld.reachabilitysize = 0;
 +	//nodes of the bsp tree
 +	if (aasworld.nodes) FreeMemory(aasworld.nodes);
 +	aasworld.nodes = NULL;
 +	aasworld.numnodes = 0;
 +	//cluster portals
 +	if (aasworld.portals) FreeMemory(aasworld.portals);
 +	aasworld.portals = NULL;
 +	aasworld.numportals = 0;
 +	//cluster portal index
 +	if (aasworld.portalindex) FreeMemory(aasworld.portalindex);
 +	aasworld.portalindex = NULL;
 +	aasworld.portalindexsize = 0;
 +	//clusters
 +	if (aasworld.clusters) FreeMemory(aasworld.clusters);
 +	aasworld.clusters = NULL;
 +	aasworld.numclusters = 0;
 +	
 +	Log_Print("freed ");
 +	PrintMemorySize(allocatedaasmem);
 +	Log_Print(" of AAS memory\n");
 +	allocatedaasmem = 0;
 +	//
 +	if (aas_vertexchain) FreeMemory(aas_vertexchain);
 +	aas_vertexchain = NULL;
 +	if (aas_planechain) FreeMemory(aas_planechain);
 +	aas_planechain = NULL;
 +	if (aas_edgechain) FreeMemory(aas_edgechain);
 +	aas_edgechain = NULL;
 +} //end of the function AAS_FreeMaxAAS
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +unsigned AAS_HashVec(vec3_t vec)
 +{
 +	int x, y;
 +
 +	x = (MAX_MAP_BOUNDS + (int)(vec[0]+0.5)) >> VERTEX_HASH_SHIFT;
 +	y = (MAX_MAP_BOUNDS + (int)(vec[1]+0.5)) >> VERTEX_HASH_SHIFT;
 +
 +	if (x < 0 || x >= VERTEX_HASH_SIZE || y < 0 || y >= VERTEX_HASH_SIZE)
 +	{
 +		Log_Print("WARNING! HashVec: point %f %f %f outside valid range\n", vec[0], vec[1], vec[2]);
 +		Log_Print("This should never happen!\n");
 +		return -1;
 +	} //end if
 +	
 +	return y*VERTEX_HASH_SIZE + x;
 +} //end of the function AAS_HashVec
 +//===========================================================================
 +// returns true if the vertex was found in the list
 +// stores the vertex number in *vnum
 +// stores a new vertex if not stored already
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_GetVertex(vec3_t v, int *vnum)
 +{
 +	int i;
 +#ifndef VERTEX_HASHING
 +	float diff;
 +#endif //VERTEX_HASHING
 +
 +#ifdef VERTEX_HASHING
 +	int h, vn;
 +	vec3_t vert;
 +	
 +	for (i = 0; i < 3; i++)
 +	{
 +		if ( fabs(v[i] - Q_rint(v[i])) < INTEGRAL_EPSILON)
 +			vert[i] = Q_rint(v[i]);
 +		else
 +			vert[i] = v[i];
 +	} //end for
 +
 +	h = AAS_HashVec(vert);
 +	//if the vertex was outside the valid range
 +	if (h == -1)
 +	{
 +		*vnum = -1;
 +		return true;
 +	} //end if
 +
 +	for (vn = aas_hashverts[h]; vn >= 0; vn = aas_vertexchain[vn])
 +	{
 +		if (fabs(aasworld.vertexes[vn][0] - vert[0]) < VERTEX_EPSILON
 +				&& fabs(aasworld.vertexes[vn][1] - vert[1]) < VERTEX_EPSILON
 +				&& fabs(aasworld.vertexes[vn][2] - vert[2]) < VERTEX_EPSILON)
 +		{
 +			*vnum = vn;
 +			return true;
 +		} //end if
 +	} //end for
 +#else //VERTEX_HASHING
 +	//check if the vertex is already stored
 +	//stupid linear search
 +	for (i = 0; i < aasworld.numvertexes; i++)
 +	{
 +		diff = vert[0] - aasworld.vertexes[i][0];
 +		if (diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON)
 +		{
 +			diff = vert[1] - aasworld.vertexes[i][1];
 +			if (diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON)
 +			{
 +				diff = vert[2] - aasworld.vertexes[i][2];
 +				if (diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON)
 +				{
 +					*vnum = i;
 +					return true;
 +				} //end if
 +			} //end if
 +		} //end if
 +	} //end for
 +#endif //VERTEX_HASHING
 +
 +	if (aasworld.numvertexes >= max_aas.max_vertexes)
 +	{
 +		Error("AAS_MAX_VERTEXES = %d", max_aas.max_vertexes);
 +	} //end if
 +	VectorCopy(vert, aasworld.vertexes[aasworld.numvertexes]);
 +	*vnum = aasworld.numvertexes;
 +
 +#ifdef VERTEX_HASHING
 +	aas_vertexchain[aasworld.numvertexes] = aas_hashverts[h];
 +	aas_hashverts[h] = aasworld.numvertexes;
 +#endif //VERTEX_HASHING
 +
 +	aasworld.numvertexes++;
 +	return false;
 +} //end of the function AAS_GetVertex
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +unsigned AAS_HashEdge(int v1, int v2)
 +{
 +	int vnum1, vnum2;
 +	//
 +	if (v1 < v2)
 +	{
 +		vnum1 = v1;
 +		vnum2 = v2;
 +	} //end if
 +	else
 +	{
 +		vnum1 = v2;
 +		vnum2 = v1;
 +	} //end else
 +	return (vnum1 + vnum2) & (EDGE_HASH_SIZE-1);
 +} //end of the function AAS_HashVec
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_AddEdgeToHash(int edgenum)
 +{
 +	int hash;
 +	aas_edge_t *edge;
 +
 +	edge = &aasworld.edges[edgenum];
 +
 +	hash = AAS_HashEdge(edge->v[0], edge->v[1]);
 +
 +	aas_edgechain[edgenum] = aas_hashedges[hash];
 +	aas_hashedges[hash] = edgenum;
 +} //end of the function AAS_AddEdgeToHash
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_FindHashedEdge(int v1num, int v2num, int *edgenum)
 +{
 +	int e, hash;
 +	aas_edge_t *edge;
 +
 +	hash = AAS_HashEdge(v1num, v2num);
 +	for (e = aas_hashedges[hash]; e >= 0; e = aas_edgechain[e])
 +	{
 +		edge = &aasworld.edges[e];
 +		if (edge->v[0] == v1num)
 +		{
 +			if (edge->v[1] == v2num)
 +			{
 +				*edgenum = e;
 +				return true;
 +			} //end if
 +		} //end if
 +		else if (edge->v[1] == v1num)
 +		{
 +			if (edge->v[0] == v2num)
 +			{
 +				//negative for a reversed edge
 +				*edgenum = -e;
 +				return true;
 +			} //end if
 +		} //end else
 +	} //end for
 +	return false;
 +} //end of the function AAS_FindHashedPlane
 +//===========================================================================
 +// returns true if the edge was found
 +// stores the edge number in *edgenum (negative if reversed edge)
 +// stores new edge if not stored already
 +// returns zero when the edge is degenerate
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_GetEdge(vec3_t v1, vec3_t v2, int *edgenum)
 +{
 +	int v1num, v2num;
 +	qboolean found;
 +
 +	//the first edge is a dummy
 +	if (aasworld.numedges == 0) aasworld.numedges = 1;
 +
 +	found = AAS_GetVertex(v1, &v1num);
 +	found &= AAS_GetVertex(v2, &v2num);
 +	//if one of the vertexes was outside the valid range
 +	if (v1num == -1 || v2num == -1)
 +	{
 +		*edgenum = 0;
 +		return true;
 +	} //end if
 +	//if both vertexes are the same or snapped onto each other
 +	if (v1num == v2num)
 +	{
 +		*edgenum = 0;
 +		return true;
 +	} //end if
 +	//if both vertexes where already stored
 +	if (found)
 +	{
 +#ifdef EDGE_HASHING
 +		if (AAS_FindHashedEdge(v1num, v2num, edgenum)) return true;
 +#else
 +		int i;
 +		for (i = 1; i < aasworld.numedges; i++)
 +		{
 +			if (aasworld.edges[i].v[0] == v1num)
 +			{
 +				if (aasworld.edges[i].v[1] == v2num)
 +				{
 +					*edgenum = i;
 +					return true;
 +				} //end if
 +			} //end if
 +			else if (aasworld.edges[i].v[1] == v1num)
 +			{
 +				if (aasworld.edges[i].v[0] == v2num)
 +				{
 +					//negative for a reversed edge
 +					*edgenum = -i;
 +					return true;
 +				} //end if
 +			} //end else
 +		} //end for
 +#endif //EDGE_HASHING
 +	} //end if
 +	if (aasworld.numedges >= max_aas.max_edges)
 +	{
 +		Error("AAS_MAX_EDGES = %d", max_aas.max_edges);
 +	} //end if
 +	aasworld.edges[aasworld.numedges].v[0] = v1num;
 +	aasworld.edges[aasworld.numedges].v[1] = v2num;
 +	*edgenum = aasworld.numedges;
 +#ifdef EDGE_HASHING
 +	AAS_AddEdgeToHash(*edgenum);
 +#endif //EDGE_HASHING
 +	aasworld.numedges++;
 +	return false;
 +} //end of the function AAS_GetEdge
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_PlaneTypeForNormal(vec3_t normal)
 +{
 +	vec_t	ax, ay, az;
 +	
 +	//NOTE: epsilon used
 +	if (	(normal[0] >= 1.0 -NORMAL_EPSILON) ||
 +			(normal[0] <= -1.0 + NORMAL_EPSILON)) return PLANE_X;
 +	if (	(normal[1] >= 1.0 -NORMAL_EPSILON) ||
 +			(normal[1] <= -1.0 + NORMAL_EPSILON)) return PLANE_Y;
 +	if (	(normal[2] >= 1.0 -NORMAL_EPSILON) ||
 +			(normal[2] <= -1.0 + NORMAL_EPSILON)) return PLANE_Z;
 +		
 +	ax = fabs(normal[0]);
 +	ay = fabs(normal[1]);
 +	az = fabs(normal[2]);
 +	
 +	if (ax >= ay && ax >= az) return PLANE_ANYX;
 +	if (ay >= ax && ay >= az) return PLANE_ANYY;
 +	return PLANE_ANYZ;
 +} //end of the function AAS_PlaneTypeForNormal
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_AddPlaneToHash(int planenum)
 +{
 +	int hash;
 +	aas_plane_t *plane;
 +
 +	plane = &aasworld.planes[planenum];
 +
 +	hash = (int)fabs(plane->dist) / 8;
 +	hash &= (PLANE_HASH_SIZE-1);
 +
 +	aas_planechain[planenum] = aas_hashplanes[hash];
 +	aas_hashplanes[hash] = planenum;
 +} //end of the function AAS_AddPlaneToHash
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_PlaneEqual(vec3_t normal, float dist, int planenum)
 +{
 +	float diff;
 +
 +	diff = dist - aasworld.planes[planenum].dist;
 +	if (diff > -DIST_EPSILON && diff < DIST_EPSILON)
 +	{
 +		diff = normal[0] - aasworld.planes[planenum].normal[0];
 +		if (diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON)
 +		{
 +			diff = normal[1] - aasworld.planes[planenum].normal[1];
 +			if (diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON)
 +			{
 +				diff = normal[2] - aasworld.planes[planenum].normal[2];
 +				if (diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON)
 +				{
 +					return true;
 +				} //end if
 +			} //end if
 +		} //end if
 +	} //end if
 +	return false;
 +} //end of the function AAS_PlaneEqual
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_FindPlane(vec3_t normal, float dist, int *planenum)
 +{
 +	int i;
 +
 +	for (i = 0; i < aasworld.numplanes; i++)
 +	{
 +		if (AAS_PlaneEqual(normal, dist, i))
 +		{
 +			*planenum = i;
 +			return true;
 +		} //end if
 +	} //end for
 +	return false;
 +} //end of the function AAS_FindPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_FindHashedPlane(vec3_t normal, float dist, int *planenum)
 +{
 +	int i, p;
 +	aas_plane_t *plane;
 +	int hash, h;
 +
 +	hash = (int)fabs(dist) / 8;
 +	hash &= (PLANE_HASH_SIZE-1);
 +
 +	//search the border bins as well
 +	for (i = -1; i <= 1; i++)
 +	{
 +		h = (hash+i)&(PLANE_HASH_SIZE-1);
 +		for (p = aas_hashplanes[h]; p >= 0; p = aas_planechain[p])
 +		{
 +			plane = &aasworld.planes[p];
 +			if (AAS_PlaneEqual(normal, dist, p))
 +			{
 +				*planenum = p;
 +				return true;
 +			} //end if
 +		} //end for
 +	} //end for
 +	return false;
 +} //end of the function AAS_FindHashedPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_GetPlane(vec3_t normal, vec_t dist, int *planenum)
 +{
 +	aas_plane_t *plane, temp;
 +
 +	//if (AAS_FindPlane(normal, dist, planenum)) return true;
 +	if (AAS_FindHashedPlane(normal, dist, planenum)) return true;
 +
 +	if (aasworld.numplanes >= max_aas.max_planes-1)
 +	{
 +		Error("AAS_MAX_PLANES = %d", max_aas.max_planes);
 +	} //end if
 +
 +#ifdef STOREPLANESDOUBLE
 +	plane = &aasworld.planes[aasworld.numplanes];
 +	VectorCopy(normal, plane->normal);
 +	plane->dist = dist;
 +	plane->type = (plane+1)->type = PlaneTypeForNormal(plane->normal);
 +
 +	VectorCopy(normal, (plane+1)->normal);
 +	VectorNegate((plane+1)->normal, (plane+1)->normal);
 +	(plane+1)->dist = -dist;
 +
 +	aasworld.numplanes += 2;
 +
 +	//allways put axial planes facing positive first
 +	if (plane->type < 3)
 +	{
 +		if (plane->normal[0] < 0 || plane->normal[1] < 0 || plane->normal[2] < 0)
 +		{
 +			// flip order
 +			temp = *plane;
 +			*plane = *(plane+1);
 +			*(plane+1) = temp;
 +			*planenum = aasworld.numplanes - 1;
 +			return false;
 +		} //end if
 +	} //end if
 +	*planenum = aasworld.numplanes - 2;
 +	//add the planes to the hash
 +	AAS_AddPlaneToHash(aasworld.numplanes - 1);
 +	AAS_AddPlaneToHash(aasworld.numplanes - 2);
 +	return false;
 +#else
 +	plane = &aasworld.planes[aasworld.numplanes];
 +	VectorCopy(normal, plane->normal);
 +	plane->dist = dist;
 +	plane->type = AAS_PlaneTypeForNormal(normal);
 +
 +	*planenum = aasworld.numplanes;
 +	aasworld.numplanes++;
 +	//add the plane to the hash
 +	AAS_AddPlaneToHash(aasworld.numplanes - 1);
 +	return false;
 +#endif //STOREPLANESDOUBLE
 +} //end of the function AAS_GetPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean AAS_GetFace(winding_t *w, plane_t *p, int side, int *facenum)
 +{
 +	int edgenum, i, j;
 +	aas_face_t *face;
 +
 +	//face zero is a dummy, because of the face index with negative numbers
 +	if (aasworld.numfaces == 0) aasworld.numfaces = 1;
 +
 +	if (aasworld.numfaces >= max_aas.max_faces)
 +	{
 +		Error("AAS_MAX_FACES = %d", max_aas.max_faces);
 +	} //end if
 +	face = &aasworld.faces[aasworld.numfaces];
 +	AAS_GetPlane(p->normal, p->dist, &face->planenum);
 +	face->faceflags = 0;
 +	face->firstedge = aasworld.edgeindexsize;
 +	face->frontarea = 0;
 +	face->backarea = 0;
 +	face->numedges = 0;
 +	for (i = 0; i < w->numpoints; i++)
 +	{
 +		if (aasworld.edgeindexsize >= max_aas.max_edgeindexsize)
 +		{
 +			Error("AAS_MAX_EDGEINDEXSIZE = %d", max_aas.max_edgeindexsize);
 +		} //end if
 +		j = (i+1) % w->numpoints;
 +		AAS_GetEdge(w->p[i], w->p[j], &edgenum);
 +		//if the edge wasn't degenerate
 +		if (edgenum)
 +		{
 +			aasworld.edgeindex[aasworld.edgeindexsize++] = edgenum;
 +			face->numedges++;
 +		} //end if
 +		else if (verbose)
 +		{
 +			Log_Write("AAS_GetFace: face %d had degenerate edge %d-%d\r\n",
 +														aasworld.numfaces, i, j);
 +		} //end else
 +	} //end for
 +	if (face->numedges < 1
 +#ifdef NOTHREEVERTEXFACES
 +		|| face->numedges < 3
 +#endif //NOTHREEVERTEXFACES
 +		)
 +	{
 +		memset(&aasworld.faces[aasworld.numfaces], 0, sizeof(aas_face_t));
 +		Log_Write("AAS_GetFace: face %d was tiny\r\n", aasworld.numfaces);
 +		return false;
 +	} //end if
 +	*facenum = aasworld.numfaces;
 +	aasworld.numfaces++;
 +	return true;
 +} //end of the function AAS_GetFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +/*
 +qboolean AAS_GetFace(winding_t *w, plane_t *p, int side, int *facenum)
 +{
 +	aas_edgeindex_t edges[1024];
 +	int planenum, numedges, i;
 +	int j, edgenum;
 +	qboolean foundplane, foundedges;
 +	aas_face_t *face;
 +
 +	//face zero is a dummy, because of the face index with negative numbers
 +	if (aasworld.numfaces == 0) aasworld.numfaces = 1;
 +
 +	foundplane = AAS_GetPlane(p->normal, p->dist, &planenum);
 +
 +	foundedges = true;
 +	numedges = w->numpoints;
 +	for (i = 0; i < w->numpoints; i++)
 +	{
 +		if (i >= 1024) Error("AAS_GetFace: more than %d edges\n", 1024);
 +		foundedges &= AAS_GetEdge(w->p[i], w->p[(i+1 >= w->numpoints ? 0 : i+1)], &edges[i]);
 +	} //end for
 +
 +	//FIXME: use portal number instead of a search
 +	//if the plane and all edges already existed
 +	if (foundplane && foundedges)
 +	{
 +		for (i = 0; i < aasworld.numfaces; i++)
 +		{
 +			face = &aasworld.faces[i];
 +			if (planenum == face->planenum)
 +			{
 +				if (numedges == face->numedges)
 +				{
 +					for (j = 0; j < numedges; j++)
 +					{
 +						edgenum = abs(aasworld.edgeindex[face->firstedge + j]);
 +						if (abs(edges[i]) != edgenum) break;
 +					} //end for
 +					if (j == numedges)
 +					{
 +						//jippy found the face
 +						*facenum = -i;
 +						return true;
 +					} //end if
 +				} //end if
 +			} //end if
 +		} //end for
 +	} //end if
 +	if (aasworld.numfaces >= max_aas.max_faces)
 +	{
 +		Error("AAS_MAX_FACES = %d", max_aas.max_faces);
 +	} //end if
 +	face = &aasworld.faces[aasworld.numfaces];
 +	face->planenum = planenum;
 +	face->faceflags = 0;
 +	face->numedges = numedges;
 +	face->firstedge = aasworld.edgeindexsize;
 +	face->frontarea = 0;
 +	face->backarea = 0;
 +	for (i = 0; i < numedges; i++)
 +	{
 +		if (aasworld.edgeindexsize >= max_aas.max_edgeindexsize)
 +		{
 +			Error("AAS_MAX_EDGEINDEXSIZE = %d", max_aas.max_edgeindexsize);
 +		} //end if
 +		aasworld.edgeindex[aasworld.edgeindexsize++] = edges[i];
 +	} //end for
 +	*facenum = aasworld.numfaces;
 +	aasworld.numfaces++;
 +	return false;
 +} //end of the function AAS_GetFace*/
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_StoreAreaSettings(tmp_areasettings_t *tmpareasettings)
 +{
 +	aas_areasettings_t *areasettings;
 +
 +	if (aasworld.numareasettings == 0) aasworld.numareasettings = 1;
 +	areasettings = &aasworld.areasettings[aasworld.numareasettings++];
 +	areasettings->areaflags = tmpareasettings->areaflags;
 +	areasettings->presencetype = tmpareasettings->presencetype;
 +	areasettings->contents = tmpareasettings->contents;
 +	if (tmpareasettings->modelnum > AREACONTENTS_MAXMODELNUM)
 +		Log_Print("WARNING: more than %d mover models\n", AREACONTENTS_MAXMODELNUM);
 +	areasettings->contents |= (tmpareasettings->modelnum & AREACONTENTS_MAXMODELNUM) << AREACONTENTS_MODELNUMSHIFT;
 +} //end of the function AAS_StoreAreaSettings
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_StoreArea(tmp_area_t *tmparea)
 +{
 +	int side, edgenum, i;
 +	plane_t *plane;
 +	tmp_face_t *tmpface;
 +	aas_area_t *aasarea;
 +	aas_edge_t *edge;
 +	aas_face_t *aasface;
 +	aas_faceindex_t aasfacenum;
 +	vec3_t facecenter;
 +	winding_t *w;
 +
 +	//when the area is merged go to the merged area
 +	//FIXME: this isn't necessary anymore because the tree
 +	//			is refreshed after area merging
 +	while(tmparea->mergedarea) tmparea = tmparea->mergedarea;
 +	//
 +	if (tmparea->invalid) Error("AAS_StoreArea: tried to store invalid area");
 +	//if there is an aas area already stored for this tmp area
 +	if (tmparea->aasareanum) return -tmparea->aasareanum;
 +	//
 +	if (aasworld.numareas >= max_aas.max_areas)
 +	{
 +		Error("AAS_MAX_AREAS = %d", max_aas.max_areas);
 +	} //end if
 +	//area zero is a dummy
 +	if (aasworld.numareas == 0) aasworld.numareas = 1;
 +	//create an area from this leaf
 +	aasarea = &aasworld.areas[aasworld.numareas];
 +	aasarea->areanum = aasworld.numareas;
 +	aasarea->numfaces = 0;
 +	aasarea->firstface = aasworld.faceindexsize;
 +	ClearBounds(aasarea->mins, aasarea->maxs);
 +	VectorClear(aasarea->center);
 +	//
 +//	Log_Write("tmparea %d became aasarea %d\r\n", tmparea->areanum, aasarea->areanum);
 +	//store the aas area number at the tmp area
 +	tmparea->aasareanum = aasarea->areanum;
 +	//
 +	for (tmpface = tmparea->tmpfaces; tmpface; tmpface = tmpface->next[side])
 +	{
 +		side = tmpface->frontarea != tmparea;
 +		//if there's an aas face created for the tmp face already
 +		if (tmpface->aasfacenum)
 +		{
 +			//we're at the back of the face so use a negative index
 +			aasfacenum = -tmpface->aasfacenum;
 +#ifdef DEBUG
 +			if (tmpface->aasfacenum < 0 || tmpface->aasfacenum > max_aas.max_faces)
 +			{
 +				Error("AAS_CreateTree_r: face number out of range");
 +			} //end if
 +#endif //DEBUG
 +			aasface = &aasworld.faces[tmpface->aasfacenum];
 +			aasface->backarea = aasarea->areanum;
 +		} //end if
 +		else
 +		{
 +			plane = &mapplanes[tmpface->planenum ^ side];
 +			if (side)
 +			{
 +				w = tmpface->winding;
 +				tmpface->winding = ReverseWinding(tmpface->winding);
 +			} //end if
 +			if (!AAS_GetFace(tmpface->winding, plane, 0, &aasfacenum)) continue;
 +			if (side)
 +			{
 +				FreeWinding(tmpface->winding);
 +				tmpface->winding = w;
 +			} //end if
 +			aasface = &aasworld.faces[aasfacenum];
 +			aasface->frontarea = aasarea->areanum;
 +			aasface->backarea = 0;
 +			aasface->faceflags = tmpface->faceflags;
 +			//set the face number at the tmp face
 +			tmpface->aasfacenum = aasfacenum;
 +		} //end else
 +		//add face points to the area bounds and
 +		//calculate the face 'center'
 +		VectorClear(facecenter);
 +		for (edgenum = 0; edgenum < aasface->numedges; edgenum++)
 +		{
 +			edge = &aasworld.edges[abs(aasworld.edgeindex[aasface->firstedge + edgenum])];
 +			for (i = 0; i < 2; i++)
 +			{
 +				AddPointToBounds(aasworld.vertexes[edge->v[i]], aasarea->mins, aasarea->maxs);
 +				VectorAdd(aasworld.vertexes[edge->v[i]], facecenter, facecenter);
 +			} //end for
 +		} //end for
 +		VectorScale(facecenter, 1.0 / (aasface->numedges * 2.0), facecenter);
 +		//add the face 'center' to the area 'center'
 +		VectorAdd(aasarea->center, facecenter, aasarea->center);
 +		//
 +		if (aasworld.faceindexsize >= max_aas.max_faceindexsize)
 +		{
 +			Error("AAS_MAX_FACEINDEXSIZE = %d", max_aas.max_faceindexsize);
 +		} //end if
 +		aasworld.faceindex[aasworld.faceindexsize++] = aasfacenum;
 +		aasarea->numfaces++;
 +	} //end for
 +	//if the area has no faces at all (return 0, = solid leaf)
 +	if (!aasarea->numfaces) return 0;
 +	//
 +	VectorScale(aasarea->center, 1.0 / aasarea->numfaces, aasarea->center);
 +	//Log_Write("area %d center %f %f %f\r\n", aasworld.numareas,
 +	//				aasarea->center[0], aasarea->center[1], aasarea->center[2]);
 +	//store the area settings
 +	AAS_StoreAreaSettings(tmparea->settings);
 +	//
 +	//Log_Write("tmp area %d became aas area %d\r\n", tmpareanum, aasarea->areanum);
 +	qprintf("\r%6d", aasarea->areanum);
 +	//
 +	aasworld.numareas++;
 +	return -(aasworld.numareas - 1);
 +} //end of the function AAS_StoreArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int AAS_StoreTree_r(tmp_node_t *tmpnode)
 +{
 +	int aasnodenum;
 +	plane_t *plane;
 +	aas_node_t *aasnode;
 +
 +	//if it is a solid leaf
 +	if (!tmpnode) return 0;
 +	//negative so it's an area
 +	if (tmpnode->tmparea) return AAS_StoreArea(tmpnode->tmparea);
 +	//it's another node
 +	//the first node is a dummy
 +	if (aasworld.numnodes == 0) aasworld.numnodes = 1;
 +	if (aasworld.numnodes >= max_aas.max_nodes)
 +	{
 +		Error("AAS_MAX_NODES = %d", max_aas.max_nodes);
 +	} //end if
 +	aasnodenum = aasworld.numnodes;
 +	aasnode = &aasworld.nodes[aasworld.numnodes++];
 +	plane = &mapplanes[tmpnode->planenum];
 +	AAS_GetPlane(plane->normal, plane->dist, &aasnode->planenum);
 +	aasnode->children[0] = AAS_StoreTree_r(tmpnode->children[0]);
 +	aasnode->children[1] = AAS_StoreTree_r(tmpnode->children[1]);
 +	return aasnodenum;
 +} //end of the function AAS_StoreTree_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_StoreBoundingBoxes(void)
 +{
 +	if (cfg.numbboxes > max_aas.max_bboxes)
 +	{
 +		Error("more than %d bounding boxes", max_aas.max_bboxes);
 +	} //end if
 +	aasworld.numbboxes = cfg.numbboxes;
 +	memcpy(aasworld.bboxes, cfg.bboxes, cfg.numbboxes * sizeof(aas_bbox_t));
 +} //end of the function AAS_StoreBoundingBoxes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_StoreFile(char *filename)
 +{
 +	AAS_AllocMaxAAS();
 +
 +	Log_Write("AAS_StoreFile\r\n");
 +	//
 +	AAS_StoreBoundingBoxes();
 +	//
 +	qprintf("%6d areas stored", 0);
 +	//start with node 1 because node zero is a dummy
 +	AAS_StoreTree_r(tmpaasworld.nodes);
 +	qprintf("\n");
 +	Log_Write("%6d areas stored\r\n", aasworld.numareas);
 +	aasworld.loaded = true;
 +} //end of the function AAS_StoreFile
 diff --git a/code/bspc/aas_store.h b/code/bspc/aas_store.h new file mode 100755 index 0000000..26957e4 --- /dev/null +++ b/code/bspc/aas_store.h @@ -0,0 +1,107 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#define AAS_MAX_BBOXES						5
 +#define AAS_MAX_VERTEXES					512000
 +#define AAS_MAX_PLANES						65536
 +#define AAS_MAX_EDGES						512000
 +#define AAS_MAX_EDGEINDEXSIZE				512000
 +#define AAS_MAX_FACES						512000
 +#define AAS_MAX_FACEINDEXSIZE				512000
 +#define AAS_MAX_AREAS						65536
 +#define AAS_MAX_AREASETTINGS				65536
 +#define AAS_MAX_REACHABILITYSIZE			65536
 +#define AAS_MAX_NODES						256000
 +#define AAS_MAX_PORTALS						65536
 +#define AAS_MAX_PORTALINDEXSIZE				65536
 +#define AAS_MAX_CLUSTERS					65536
 +
 +#define BSPCINCLUDE
 +#include "../game/be_aas.h"
 +#include "../botlib/be_aas_def.h"
 +
 +/*
 +typedef struct bspc_aas_s
 +{
 +	int loaded;
 +	int initialized;								//true when AAS has been initialized
 +	int savefile;									//set true when file should be saved
 +	//bounding boxes
 +	int numbboxes;
 +	aas_bbox_t *bboxes;
 +	//vertexes
 +	int numvertexes;
 +	aas_vertex_t *vertexes;
 +	//planes
 +	int numplanes;
 +	aas_plane_t *planes;
 +	//edges
 +	int numedges;
 +	aas_edge_t *edges;
 +	//edge index
 +	int edgeindexsize;
 +	aas_edgeindex_t *edgeindex;
 +	//faces
 +	int numfaces;
 +	aas_face_t *faces;
 +	//face index
 +	int faceindexsize;
 +	aas_faceindex_t *faceindex;
 +	//convex areas
 +	int numareas;
 +	aas_area_t *areas;
 +	//convex area settings
 +	int numareasettings;
 +	aas_areasettings_t *areasettings;
 +	//reachablity list
 +	int reachabilitysize;
 +	aas_reachability_t *reachability;
 +	//nodes of the bsp tree
 +	int numnodes;
 +	aas_node_t *nodes;
 +	//cluster portals
 +	int numportals;
 +	aas_portal_t *portals;
 +	//cluster portal index
 +	int portalindexsize;
 +	aas_portalindex_t *portalindex;
 +	//clusters
 +	int numclusters;
 +	aas_cluster_t *clusters;
 +	//
 +	int numreachabilityareas;
 +	float reachabilitytime;
 +} bspc_aas_t;
 +
 +extern bspc_aas_t aasworld;
 +//*/
 +
 +extern aas_t aasworld;
 +
 +//stores the AAS file from the temporary AAS
 +void AAS_StoreFile(char *filename);
 +//returns a number of the given plane
 +qboolean AAS_FindPlane(vec3_t normal, float dist, int *planenum);
 +//allocates the maximum AAS memory for storage
 +void AAS_AllocMaxAAS(void);
 +//frees the maximum AAS memory for storage
 +void AAS_FreeMaxAAS(void);
 diff --git a/code/bspc/aasfile.h b/code/bspc/aasfile.h new file mode 100755 index 0000000..7ed8677 --- /dev/null +++ b/code/bspc/aasfile.h @@ -0,0 +1,252 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +
 +//NOTE:	int =	default signed
 +//				default long
 +
 +#define AASID						(('S'<<24)+('A'<<16)+('A'<<8)+'E')
 +#define AASVERSION_OLD				4
 +#define AASVERSION					5
 +
 +//presence types
 +#define PRESENCE_NONE				1
 +#define PRESENCE_NORMAL				2
 +#define PRESENCE_CROUCH				4
 +
 +//travel types
 +#define MAX_TRAVELTYPES				32
 +#define TRAVEL_INVALID				1		//temporary not possible
 +#define TRAVEL_WALK					2		//walking
 +#define TRAVEL_CROUCH				3		//crouching
 +#define TRAVEL_BARRIERJUMP			4		//jumping onto a barrier
 +#define TRAVEL_JUMP					5		//jumping
 +#define TRAVEL_LADDER				6		//climbing a ladder
 +#define TRAVEL_WALKOFFLEDGE			7		//walking of a ledge
 +#define TRAVEL_SWIM					8		//swimming
 +#define TRAVEL_WATERJUMP			9		//jump out of the water
 +#define TRAVEL_TELEPORT				10		//teleportation
 +#define TRAVEL_ELEVATOR				11		//travel by elevator
 +#define TRAVEL_ROCKETJUMP			12		//rocket jumping required for travel
 +#define TRAVEL_BFGJUMP				13		//bfg jumping required for travel
 +#define TRAVEL_GRAPPLEHOOK			14		//grappling hook required for travel
 +#define TRAVEL_DOUBLEJUMP			15		//double jump
 +#define TRAVEL_RAMPJUMP				16		//ramp jump
 +#define TRAVEL_STRAFEJUMP			17		//strafe jump
 +#define TRAVEL_JUMPPAD				18		//jump pad
 +#define TRAVEL_FUNCBOB				19		//func bob
 +
 +//face flags
 +#define FACE_SOLID					1		//just solid at the other side
 +#define FACE_LADDER					2		//ladder
 +#define FACE_GROUND					4		//standing on ground when in this face
 +#define FACE_GAP					8		//gap in the ground
 +#define FACE_LIQUID					16
 +#define FACE_LIQUIDSURFACE			32
 +
 +//area contents
 +#define AREACONTENTS_WATER				1
 +#define AREACONTENTS_LAVA				2
 +#define AREACONTENTS_SLIME				4
 +#define AREACONTENTS_CLUSTERPORTAL		8
 +#define AREACONTENTS_TELEPORTAL			16
 +#define AREACONTENTS_ROUTEPORTAL		32
 +#define AREACONTENTS_TELEPORTER			64
 +#define AREACONTENTS_JUMPPAD			128
 +#define AREACONTENTS_DONOTENTER			256
 +#define	AREACONTENTS_VIEWPORTAL			512
 +
 +//area flags
 +#define AREA_GROUNDED				1		//bot can stand on the ground
 +#define AREA_LADDER					2		//area contains one or more ladder faces
 +#define AREA_LIQUID					4		//area contains a liquid
 +
 +//aas file header lumps
 +#define AAS_LUMPS					14
 +#define AASLUMP_BBOXES				0
 +#define AASLUMP_VERTEXES			1
 +#define AASLUMP_PLANES				2
 +#define AASLUMP_EDGES				3
 +#define AASLUMP_EDGEINDEX			4
 +#define AASLUMP_FACES				5
 +#define AASLUMP_FACEINDEX			6
 +#define AASLUMP_AREAS				7
 +#define AASLUMP_AREASETTINGS		8
 +#define AASLUMP_REACHABILITY		9
 +#define AASLUMP_NODES				10
 +#define AASLUMP_PORTALS				11
 +#define AASLUMP_PORTALINDEX			12
 +#define AASLUMP_CLUSTERS			13
 +
 +//========== bounding box =========
 +
 +//bounding box
 +typedef struct aas_bbox_s
 +{
 +	int presencetype;
 +	int flags;
 +	vec3_t mins, maxs;
 +} aas_bbox_t;
 +
 +//============ settings ===========
 +
 +//reachability to another area
 +typedef struct aas_reachability_s
 +{
 +	int areanum;						//number of the reachable area
 +	int facenum;						//number of the face towards the other area
 +	int edgenum;						//number of the edge towards the other area
 +	vec3_t start;						//start point of inter area movement
 +	vec3_t end;							//end point of inter area movement
 +	int traveltype;					//type of travel required to get to the area
 +	unsigned short int traveltime;//travel time of the inter area movement
 +} aas_reachability_t;
 +
 +//area settings
 +typedef struct aas_areasettings_s
 +{
 +	//could also add all kind of statistic fields
 +	int contents;						//contents of the convex area
 +	int areaflags;						//several area flags
 +	int presencetype;					//how a bot can be present in this convex area
 +	int cluster;						//cluster the area belongs to, if negative it's a portal
 +	int clusterareanum;				//number of the area in the cluster
 +	int numreachableareas;			//number of reachable areas from this one
 +	int firstreachablearea;			//first reachable area in the reachable area index
 +} aas_areasettings_t;
 +
 +//cluster portal
 +typedef struct aas_portal_s
 +{
 +	int areanum;						//area that is the actual portal
 +	int frontcluster;					//cluster at front of portal
 +	int backcluster;					//cluster at back of portal
 +	int clusterareanum[2];			//number of the area in the front and back cluster
 +} aas_portal_t;
 +
 +//cluster portal index
 +typedef int aas_portalindex_t;
 +
 +//cluster
 +typedef struct aas_cluster_s
 +{
 +	int numareas;						//number of areas in the cluster
 +	int numreachabilityareas;			//number of areas with reachabilities
 +	int numportals;						//number of cluster portals
 +	int firstportal;					//first cluster portal in the index
 +} aas_cluster_t;
 +
 +//============ 3d definition ============
 +
 +typedef vec3_t aas_vertex_t;
 +
 +//just a plane in the third dimension
 +typedef struct aas_plane_s
 +{
 +	vec3_t normal;						//normal vector of the plane
 +	float dist;							//distance of the plane (normal vector * distance = point in plane)
 +	int type;
 +} aas_plane_t;
 +
 +//edge
 +typedef struct aas_edge_s
 +{
 +	int v[2];							//numbers of the vertexes of this edge
 +} aas_edge_t;
 +
 +//edge index, negative if vertexes are reversed
 +typedef int aas_edgeindex_t;
 +
 +//a face bounds a convex area, often it will also seperate two convex areas
 +typedef struct aas_face_s
 +{
 +	int planenum;						//number of the plane this face is in
 +	int faceflags;						//face flags (no use to create face settings for just this field)
 +	int numedges;						//number of edges in the boundary of the face
 +	int firstedge;						//first edge in the edge index
 +	int frontarea;						//convex area at the front of this face
 +	int backarea;						//convex area at the back of this face
 +} aas_face_t;
 +
 +//face index, stores a negative index if backside of face
 +typedef int aas_faceindex_t;
 +
 +//convex area with a boundary of faces
 +typedef struct aas_area_s
 +{
 +	int areanum;						//number of this area
 +	//3d definition
 +	int numfaces;						//number of faces used for the boundary of the convex area
 +	int firstface;						//first face in the face index used for the boundary of the convex area
 +	vec3_t mins;						//mins of the convex area
 +	vec3_t maxs;						//maxs of the convex area
 +	vec3_t center;						//'center' of the convex area
 +} aas_area_t;
 +
 +//nodes of the bsp tree
 +typedef struct aas_node_s
 +{
 +	int planenum;
 +	int children[2];					//child nodes of this node, or convex areas as leaves when negative
 +										//when a child is zero it's a solid leaf
 +} aas_node_t;
 +
 +//=========== aas file ===============
 +
 +//header lump
 +typedef struct
 +{
 +	int fileofs;
 +	int filelen;
 +} aas_lump_t;
 +
 +//aas file header
 +typedef struct aas_header_s
 +{
 +	int ident;
 +	int version;
 +	int bspchecksum;
 +	//data entries
 +	aas_lump_t lumps[AAS_LUMPS];
 +} aas_header_t;
 +
 +
 +//====== additional information ======
 +/*
 +
 +-	when a node child is a solid leaf the node child number is zero
 +-	two adjacent areas (sharing a plane at opposite sides) share a face
 +	this face is a portal between the areas
 +-	when an area uses a face from the faceindex with a positive index
 +	then the face plane normal points into the area
 +-	the face edges are stored counter clockwise using the edgeindex
 +-	two adjacent convex areas (sharing a face) only share One face
 +	this is a simple result of the areas being convex
 +-	the convex areas can't have a mixture of ground and gap faces
 +	other mixtures of faces in one area are allowed
 +-	areas with the AREACONTENTS_CLUSTERPORTAL in the settings have
 +	cluster number zero
 +-	edge zero is a dummy
 +-	face zero is a dummy
 +-	area zero is a dummy
 +-	node zero is a dummy
 +*/
 diff --git a/code/bspc/be_aas_bspc.c b/code/bspc/be_aas_bspc.c new file mode 100755 index 0000000..127549c --- /dev/null +++ b/code/bspc/be_aas_bspc.c @@ -0,0 +1,292 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "../game/q_shared.h"
 +#include "../bspc/l_log.h"
 +#include "../bspc/l_qfiles.h"
 +#include "../botlib/l_memory.h"
 +#include "../botlib/l_script.h"
 +#include "../botlib/l_precomp.h"
 +#include "../botlib/l_struct.h"
 +#include "../botlib/aasfile.h"
 +#include "../game/botlib.h"
 +#include "../game/be_aas.h"
 +#include "../botlib/be_aas_def.h"
 +#include "../qcommon/cm_public.h"
 +
 +//#define BSPC
 +
 +extern botlib_import_t botimport;
 +extern	qboolean capsule_collision;
 +
 +botlib_import_t botimport;
 +clipHandle_t worldmodel;
 +
 +void Error (char *error, ...);
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_Error(char *fmt, ...)
 +{
 +	va_list argptr;
 +	char text[1024];
 +
 +	va_start(argptr, fmt);
 +	vsprintf(text, fmt, argptr);
 +	va_end(argptr);
 +
 +	Error(text);
 +} //end of the function AAS_Error
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Sys_MilliSeconds(void)
 +{
 +	return clock() * 1000 / CLOCKS_PER_SEC;
 +} //end of the function Sys_MilliSeconds
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_DebugLine(vec3_t start, vec3_t end, int color)
 +{
 +} //end of the function AAS_DebugLine
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_ClearShownDebugLines(void)
 +{
 +} //end of the function AAS_ClearShownDebugLines
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +char *BotImport_BSPEntityData(void)
 +{
 +	return CM_EntityString();
 +} //end of the function AAS_GetEntityData
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask)
 +{
 +	trace_t result;
 +
 +	CM_BoxTrace(&result, start, end, mins, maxs, worldmodel, contentmask, capsule_collision);
 +
 +	bsptrace->allsolid = result.allsolid;
 +	bsptrace->contents = result.contents;
 +	VectorCopy(result.endpos, bsptrace->endpos);
 +	bsptrace->ent = result.entityNum;
 +	bsptrace->fraction = result.fraction;
 +	bsptrace->exp_dist = 0;
 +	bsptrace->plane.dist = result.plane.dist;
 +	VectorCopy(result.plane.normal, bsptrace->plane.normal);
 +	bsptrace->plane.signbits = result.plane.signbits;
 +	bsptrace->plane.type = result.plane.type;
 +	bsptrace->sidenum = 0;
 +	bsptrace->startsolid = result.startsolid;
 +	bsptrace->surface.flags = result.surfaceFlags;
 +} //end of the function BotImport_Trace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int BotImport_PointContents(vec3_t p)
 +{
 +	return CM_PointContents(p, worldmodel);
 +} //end of the function BotImport_PointContents
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *BotImport_GetMemory(int size)
 +{
 +	return GetMemory(size);
 +} //end of the function BotImport_GetMemory
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void BotImport_Print(int type, char *fmt, ...)
 +{
 +	va_list argptr;
 +	char buf[1024];
 +
 +	va_start(argptr, fmt);
 +	vsprintf(buf, fmt, argptr);
 +	printf(buf);
 +	if (buf[0] != '\r') Log_Write(buf);
 +	va_end(argptr);
 +} //end of the function BotImport_Print
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin)
 +{
 +	clipHandle_t h;
 +	vec3_t mins, maxs;
 +	float max;
 +	int	i;
 +
 +	h = CM_InlineModel(modelnum);
 +	CM_ModelBounds(h, mins, maxs);
 +	//if the model is rotated
 +	if ((angles[0] || angles[1] || angles[2]))
 +	{	// expand for rotation
 +
 +		max = RadiusFromBounds(mins, maxs);
 +		for (i = 0; i < 3; i++)
 +		{
 +			mins[i] = (mins[i] + maxs[i]) * 0.5 - max;
 +			maxs[i] = (mins[i] + maxs[i]) * 0.5 + max;
 +		} //end for
 +	} //end if
 +	if (outmins) VectorCopy(mins, outmins);
 +	if (outmaxs) VectorCopy(maxs, outmaxs);
 +	if (origin) VectorClear(origin);
 +} //end of the function BotImport_BSPModelMinsMaxsOrigin
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Com_DPrintf(char *fmt, ...)
 +{
 +	va_list argptr;
 +	char buf[1024];
 +
 +	va_start(argptr, fmt);
 +	vsprintf(buf, fmt, argptr);
 +	printf(buf);
 +	if (buf[0] != '\r') Log_Write(buf);
 +	va_end(argptr);
 +} //end of the function Com_DPrintf
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int COM_Compress( char *data_p ) {
 +	return strlen(data_p);
 +}
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Com_Memset (void* dest, const int val, const size_t count) {
 +	memset(dest, val, count);
 +}
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Com_Memcpy (void* dest, const void* src, const size_t count) {
 +	memcpy(dest, src, count);
 +}
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_InitBotImport(void)
 +{
 +	botimport.BSPEntityData = BotImport_BSPEntityData;
 +	botimport.GetMemory = BotImport_GetMemory;
 +	botimport.FreeMemory = FreeMemory;
 +	botimport.Trace = BotImport_Trace;
 +	botimport.PointContents = BotImport_PointContents;
 +	botimport.Print = BotImport_Print;
 +	botimport.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin;
 +} //end of the function AAS_InitBotImport
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_CalcReachAndClusters(struct quakefile_s *qf)
 +{
 +	float time;
 +
 +	Log_Print("loading collision map...\n");
 +	//
 +	if (!qf->pakfile[0]) strcpy(qf->pakfile, qf->filename);
 +	//load the map
 +	CM_LoadMap((char *) qf, qfalse, &aasworld.bspchecksum);
 +	//get a handle to the world model
 +	worldmodel = CM_InlineModel(0);		// 0 = world, 1 + are bmodels
 +	//initialize bot import structure
 +	AAS_InitBotImport();
 +	//load the BSP entity string
 +	AAS_LoadBSPFile();
 +	//init physics settings
 +	AAS_InitSettings();
 +	//initialize AAS link heap
 +	AAS_InitAASLinkHeap();
 +	//initialize the AAS linked entities for the new map
 +	AAS_InitAASLinkedEntities();
 +	//reset all reachabilities and clusters
 +	aasworld.reachabilitysize = 0;
 +	aasworld.numclusters = 0;
 +	//set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach)
 +	AAS_SetViewPortalsAsClusterPortals();
 +	//calculate reachabilities
 +	AAS_InitReachability();
 +	time = 0;
 +	while(AAS_ContinueInitReachability(time)) time++;
 +	//calculate clusters
 +	AAS_InitClustering();
 +} //end of the function AAS_CalcReachAndClusters
 diff --git a/code/bspc/be_aas_bspc.h b/code/bspc/be_aas_bspc.h new file mode 100755 index 0000000..72e488a --- /dev/null +++ b/code/bspc/be_aas_bspc.h @@ -0,0 +1,23 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void AAS_CalcReachAndClusters(struct quakefile_s *qf);
 diff --git a/code/bspc/brushbsp.c b/code/bspc/brushbsp.c new file mode 100755 index 0000000..22cb0dd --- /dev/null +++ b/code/bspc/brushbsp.c @@ -0,0 +1,1871 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_store.h"
 +#include "aas_cfg.h"
 +
 +#include <assert.h>
 +
 +/*
 +each side has a count of the other sides it splits
 +
 +the best split will be the one that minimizes the total split counts
 +of all remaining sides
 +
 +precalc side on plane table
 +
 +evaluate split side
 +{
 +cost = 0
 +for all sides
 +	for all sides
 +		get 
 +		if side splits side and splitside is on same child
 +			cost++;
 +}
 +*/
 +
 +int c_nodes;
 +int c_nonvis;
 +int c_active_brushes;
 +int c_solidleafnodes;
 +int c_totalsides;
 +int c_brushmemory;
 +int c_peak_brushmemory;
 +int c_nodememory;
 +int c_peak_totalbspmemory;
 +
 +// if a brush just barely pokes onto the other side,
 +// let it slide by without chopping
 +#define	PLANESIDE_EPSILON	0.001
 +//0.1
 +
 +//#ifdef DEBUG
 +typedef struct cname_s
 +{
 +	int value;
 +	char *name;
 +} cname_t;
 +
 +cname_t contentnames[] =
 +{
 +	{CONTENTS_SOLID,"CONTENTS_SOLID"},
 +	{CONTENTS_WINDOW,"CONTENTS_WINDOW"},
 +	{CONTENTS_AUX,"CONTENTS_AUX"},
 +	{CONTENTS_LAVA,"CONTENTS_LAVA"},
 +	{CONTENTS_SLIME,"CONTENTS_SLIME"},
 +	{CONTENTS_WATER,"CONTENTS_WATER"},
 +	{CONTENTS_MIST,"CONTENTS_MIST"},
 +	{LAST_VISIBLE_CONTENTS,"LAST_VISIBLE_CONTENTS"},
 +
 +	{CONTENTS_AREAPORTAL,"CONTENTS_AREAPORTAL"},
 +	{CONTENTS_PLAYERCLIP,"CONTENTS_PLAYERCLIP"},
 +	{CONTENTS_MONSTERCLIP,"CONTENTS_MONSTERCLIP"},
 +	{CONTENTS_CURRENT_0,"CONTENTS_CURRENT_0"},
 +	{CONTENTS_CURRENT_90,"CONTENTS_CURRENT_90"},
 +	{CONTENTS_CURRENT_180,"CONTENTS_CURRENT_180"},
 +	{CONTENTS_CURRENT_270,"CONTENTS_CURRENT_270"},
 +	{CONTENTS_CURRENT_UP,"CONTENTS_CURRENT_UP"},
 +	{CONTENTS_CURRENT_DOWN,"CONTENTS_CURRENT_DOWN"},
 +	{CONTENTS_ORIGIN,"CONTENTS_ORIGIN"},
 +	{CONTENTS_MONSTER,"CONTENTS_MONSTER"},
 +	{CONTENTS_DEADMONSTER,"CONTENTS_DEADMONSTER"},
 +	{CONTENTS_DETAIL,"CONTENTS_DETAIL"},
 +	{CONTENTS_Q2TRANSLUCENT,"CONTENTS_TRANSLUCENT"},
 +	{CONTENTS_LADDER,"CONTENTS_LADDER"},
 +	{0, 0}
 +};
 +
 +void PrintContents(int contents)
 +{
 +	int i;
 +
 +	for (i = 0; contentnames[i].value; i++)
 +	{
 +		if (contents & contentnames[i].value)
 +		{
 +			Log_Write("%s,", contentnames[i].name);
 +		} //end if
 +	} //end for
 +} //end of the function PrintContents
 +
 +//#endif DEBUG
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ResetBrushBSP(void)
 +{
 +	c_nodes = 0;
 +	c_nonvis = 0;
 +	c_active_brushes = 0;
 +	c_solidleafnodes = 0;
 +	c_totalsides = 0;
 +	c_brushmemory = 0;
 +	c_peak_brushmemory = 0;
 +	c_nodememory = 0;
 +	c_peak_totalbspmemory = 0;
 +} //end of the function ResetBrushBSP
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void FindBrushInTree (node_t *node, int brushnum)
 +{
 +	bspbrush_t	*b;
 +
 +	if (node->planenum == PLANENUM_LEAF)
 +	{
 +		for (b=node->brushlist ; b ; b=b->next)
 +			if (b->original->brushnum == brushnum)
 +				Log_Print ("here\n");
 +		return;
 +	}
 +	FindBrushInTree(node->children[0], brushnum);
 +	FindBrushInTree(node->children[1], brushnum);
 +} //end of the function FindBrushInTree
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void DrawBrushList (bspbrush_t *brush, node_t *node)
 +{
 +	int		i;
 +	side_t	*s;
 +
 +	GLS_BeginScene ();
 +	for ( ; brush ; brush=brush->next)
 +	{
 +		for (i=0 ; i<brush->numsides ; i++)
 +		{
 +			s = &brush->sides[i];
 +			if (!s->winding)
 +				continue;
 +			if (s->texinfo == TEXINFO_NODE)
 +				GLS_Winding (s->winding, 1);
 +			else if (!(s->flags & SFL_VISIBLE))
 +				GLS_Winding (s->winding, 2);
 +			else
 +				GLS_Winding (s->winding, 0);
 +		}
 +	}
 +	GLS_EndScene ();
 +} //end of the function DrawBrushList
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
 +{
 +	int		i;
 +	side_t	*s;
 +	FILE	*f;
 +
 +	qprintf ("writing %s\n", name);
 +	f = SafeOpenWrite (name);
 +
 +	for ( ; brush ; brush=brush->next)
 +	{
 +		for (i=0 ; i<brush->numsides ; i++)
 +		{
 +			s = &brush->sides[i];
 +			if (!s->winding)
 +				continue;
 +			if (onlyvis && !(s->flags & SFL_VISIBLE))
 +				continue;
 +			OutputWinding (brush->sides[i].winding, f);
 +		}
 +	}
 +
 +	fclose (f);
 +} //end of the function WriteBrushList
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintBrush (bspbrush_t *brush)
 +{
 +	int		i;
 +
 +	printf ("brush: %p\n", brush);
 +	for (i=0;i<brush->numsides ; i++)
 +	{
 +		pw(brush->sides[i].winding);
 +		printf ("\n");
 +	} //end for
 +} //end of the function PrintBrush
 +//===========================================================================
 +// Sets the mins/maxs based on the windings
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void BoundBrush (bspbrush_t *brush)
 +{
 +	int			i, j;
 +	winding_t	*w;
 +
 +	ClearBounds (brush->mins, brush->maxs);
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (!w)
 +			continue;
 +		for (j=0 ; j<w->numpoints ; j++)
 +			AddPointToBounds (w->p[j], brush->mins, brush->maxs);
 +	}
 +} //end of the function BoundBrush
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void CreateBrushWindings (bspbrush_t *brush)
 +{
 +	int			i, j;
 +	winding_t	*w;
 +	side_t		*side;
 +	plane_t		*plane;
 +
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		side = &brush->sides[i];
 +		plane = &mapplanes[side->planenum];
 +		w = BaseWindingForPlane (plane->normal, plane->dist);
 +		for (j=0 ; j<brush->numsides && w; j++)
 +		{
 +			if (i == j)
 +				continue;
 +			if (brush->sides[j].flags & SFL_BEVEL)
 +				continue;
 +			plane = &mapplanes[brush->sides[j].planenum^1];
 +			ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
 +		}
 +
 +		side->winding = w;
 +	}
 +
 +	BoundBrush (brush);
 +} //end of the function CreateBrushWindings
 +//===========================================================================
 +// Creates a new axial brush
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t	*BrushFromBounds (vec3_t mins, vec3_t maxs)
 +{
 +	bspbrush_t *b;
 +	int i;
 +	vec3_t normal;
 +	vec_t dist;
 +
 +	b = AllocBrush (6);
 +	b->numsides = 6;
 +	for (i=0 ; i<3 ; i++)
 +	{
 +		VectorClear (normal);
 +		normal[i] = 1;
 +		dist = maxs[i];
 +		b->sides[i].planenum = FindFloatPlane (normal, dist);
 +
 +		normal[i] = -1;
 +		dist = -mins[i];
 +		b->sides[3+i].planenum = FindFloatPlane (normal, dist);
 +	}
 +
 +	CreateBrushWindings (b);
 +
 +	return b;
 +} //end of the function BrushFromBounds
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int BrushOutOfBounds(bspbrush_t *brush, vec3_t mins, vec3_t maxs, float epsilon)
 +{
 +	int i, j, n;
 +	winding_t *w;
 +	side_t *side;
 +
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		side = &brush->sides[i];
 +		w = side->winding;
 +		for (j = 0; j < w->numpoints; j++)
 +		{
 +			for (n = 0; n < 3; n++)
 +			{
 +				if (w->p[j][n] < (mins[n] + epsilon) || w->p[j][n] > (maxs[n] - epsilon)) return true;
 +			} //end for
 +		} //end for
 +	} //end for
 +	return false;
 +} //end of the function BrushOutOfBounds
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +vec_t BrushVolume (bspbrush_t *brush)
 +{
 +	int i;
 +	winding_t *w;
 +	vec3_t corner;
 +	vec_t d, area, volume;
 +	plane_t *plane;
 +
 +	if (!brush) return 0;
 +
 +	// grab the first valid point as the corner
 +	w = NULL;
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (w) break;
 +	} //end for
 +	if (!w) return 0;
 +	VectorCopy (w->p[0], corner);
 +
 +	// make tetrahedrons to all other faces
 +	volume = 0;
 +	for ( ; i < brush->numsides; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (!w) continue;
 +		plane = &mapplanes[brush->sides[i].planenum];
 +		d = -(DotProduct (corner, plane->normal) - plane->dist);
 +		area = WindingArea(w);
 +		volume += d * area;
 +	} //end for
 +
 +	volume /= 3;
 +	return volume;
 +} //end of the function BrushVolume
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int CountBrushList (bspbrush_t *brushes)
 +{
 +	int c;
 +
 +	c = 0;
 +	for ( ; brushes; brushes = brushes->next) c++;
 +	return c;
 +} //end of the function CountBrushList
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +node_t *AllocNode (void)
 +{
 +	node_t	*node;
 +
 +	node = GetMemory(sizeof(*node));
 +	memset (node, 0, sizeof(*node));
 +	if (numthreads == 1)
 +	{
 +		c_nodememory += MemorySize(node);
 +	} //end if
 +	return node;
 +} //end of the function AllocNode
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *AllocBrush (int numsides)
 +{
 +	bspbrush_t	*bb;
 +	int			c;
 +
 +	c = (int)&(((bspbrush_t *)0)->sides[numsides]);
 +	bb = GetMemory(c);
 +	memset (bb, 0, c);
 +	if (numthreads == 1)
 +	{
 +		c_active_brushes++;
 +		c_brushmemory += MemorySize(bb);
 +		if (c_brushmemory > c_peak_brushmemory)
 +				c_peak_brushmemory = c_brushmemory;
 +	} //end if
 +	return bb;
 +} //end of the function AllocBrush
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void FreeBrush (bspbrush_t *brushes)
 +{
 +	int			i;
 +
 +	for (i=0 ; i<brushes->numsides ; i++)
 +		if (brushes->sides[i].winding)
 +			FreeWinding(brushes->sides[i].winding);
 +	if (numthreads == 1)
 +	{
 +		c_active_brushes--;
 +		c_brushmemory -= MemorySize(brushes);
 +		if (c_brushmemory < 0) c_brushmemory = 0;
 +	} //end if
 +	FreeMemory(brushes);
 +} //end of the function FreeBrush
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void FreeBrushList (bspbrush_t *brushes)
 +{
 +	bspbrush_t	*next;
 +
 +	for ( ; brushes; brushes = next)
 +	{
 +		next = brushes->next;
 +
 +		FreeBrush(brushes);
 +	} //end for
 +} //end of the function FreeBrushList
 +//===========================================================================
 +// Duplicates the brush, the sides, and the windings
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *CopyBrush (bspbrush_t *brush)
 +{
 +	bspbrush_t *newbrush;
 +	int			size;
 +	int			i;
 +	
 +	size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
 +
 +	newbrush = AllocBrush (brush->numsides);
 +	memcpy (newbrush, brush, size);
 +
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		if (brush->sides[i].winding)
 +			newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
 +	}
 +
 +	return newbrush;
 +} //end of the function CopyBrush
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +node_t *PointInLeaf (node_t *node, vec3_t point)
 +{
 +	vec_t		d;
 +	plane_t		*plane;
 +
 +	while (node->planenum != PLANENUM_LEAF)
 +	{
 +		plane = &mapplanes[node->planenum];
 +		d = DotProduct (point, plane->normal) - plane->dist;
 +		if (d > 0)
 +			node = node->children[0];
 +		else
 +			node = node->children[1];
 +	}
 +
 +	return node;
 +} //end of the function PointInLeaf
 +//===========================================================================
 +// Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +#if 0
 +int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane)
 +{
 +	int		side;
 +	int		i;
 +	vec3_t	corners[2];
 +	vec_t	dist1, dist2;
 +
 +	// axial planes are easy
 +	if (plane->type < 3)
 +	{
 +		side = 0;
 +		if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
 +			side |= PSIDE_FRONT;
 +		if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
 +			side |= PSIDE_BACK;
 +		return side;
 +	}
 +
 +	// create the proper leading and trailing verts for the box
 +
 +	for (i=0 ; i<3 ; i++)
 +	{
 +		if (plane->normal[i] < 0)
 +		{
 +			corners[0][i] = mins[i];
 +			corners[1][i] = maxs[i];
 +		}
 +		else
 +		{
 +			corners[1][i] = mins[i];
 +			corners[0][i] = maxs[i];
 +		}
 +	}
 +
 +	dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
 +	dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
 +	side = 0;
 +	if (dist1 >= PLANESIDE_EPSILON)
 +		side = PSIDE_FRONT;
 +	if (dist2 < PLANESIDE_EPSILON)
 +		side |= PSIDE_BACK;
 +
 +	return side;
 +}
 +#else
 +int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, plane_t *p)
 +{
 +	float	dist1, dist2;
 +	int sides;
 +
 +	// axial planes are easy
 +	if (p->type < 3)
 +	{
 +		sides = 0;
 +		if (emaxs[p->type] > p->dist+PLANESIDE_EPSILON) sides |= PSIDE_FRONT;
 +		if (emins[p->type] < p->dist-PLANESIDE_EPSILON) sides |= PSIDE_BACK;
 +		return sides;
 +	} //end if
 +	
 +// general case
 +	switch (p->signbits)
 +	{
 +	case 0:
 +		dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
 +		dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
 +		break;
 +	case 1:
 +		dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
 +		dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
 +		break;
 +	case 2:
 +		dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
 +		dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
 +		break;
 +	case 3:
 +		dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
 +		dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
 +		break;
 +	case 4:
 +		dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
 +		dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
 +		break;
 +	case 5:
 +		dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
 +		dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
 +		break;
 +	case 6:
 +		dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
 +		dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
 +		break;
 +	case 7:
 +		dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
 +		dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
 +		break;
 +	default:
 +		dist1 = dist2 = 0;		// shut up compiler
 +//		assert( 0 );
 +		break;
 +	}
 +
 +	sides = 0;
 +	if (dist1 - p->dist >= PLANESIDE_EPSILON) sides = PSIDE_FRONT;
 +	if (dist2 - p->dist < PLANESIDE_EPSILON) sides |= PSIDE_BACK;
 +
 +//	assert(sides != 0);
 +
 +	return sides;
 +}
 +#endif
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
 +{
 +	int i, num;
 +	plane_t *plane;
 +	int s;
 +
 +	*numsplits = 0;
 +
 +	plane = &mapplanes[planenum];
 +
 +#ifdef ME
 +	//fast axial cases
 +	if (plane->type < 3)
 +	{
 +		if (plane->dist + PLANESIDE_EPSILON < brush->mins[plane->type])
 +			return PSIDE_FRONT;
 +		if (plane->dist - PLANESIDE_EPSILON > brush->maxs[plane->type])
 +			return PSIDE_BACK;
 +	} //end if
 +#endif //ME*/
 +
 +	// if the brush actually uses the planenum,
 +	// we can tell the side for sure
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		num = brush->sides[i].planenum;
 +		if (num >= MAX_MAPFILE_PLANES)
 +			Error ("bad planenum");
 +		if (num == planenum)
 +			return PSIDE_BACK|PSIDE_FACING;
 +		if (num == (planenum ^ 1) )
 +			return PSIDE_FRONT|PSIDE_FACING;
 +
 +	}
 +
 +	// box on plane side
 +	s = BoxOnPlaneSide (brush->mins, brush->maxs, plane);
 +
 +	// if both sides, count the visible faces split
 +	if (s == PSIDE_BOTH)
 +	{
 +		*numsplits += 3;
 +	}
 +
 +	return s;
 +} //end of the function QuickTestBrushToPlanenum
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TestBrushToPlanenum (bspbrush_t *brush, int planenum,
 +						 int *numsplits, qboolean *hintsplit, int *epsilonbrush)
 +{
 +	int i, j, num;
 +	plane_t *plane;
 +	int s = 0;
 +	winding_t *w;
 +	vec_t d, d_front, d_back;
 +	int front, back;
 +	int type;
 +	float dist;
 +
 +	*numsplits = 0;
 +	*hintsplit = false;
 +
 +	plane = &mapplanes[planenum];
 +
 +#ifdef ME
 +	//fast axial cases
 +	type = plane->type;
 +	if (type < 3)
 +	{
 +		dist = plane->dist;
 +		if (dist + PLANESIDE_EPSILON < brush->mins[type]) return PSIDE_FRONT;
 +		if (dist - PLANESIDE_EPSILON > brush->maxs[type]) return PSIDE_BACK;
 +		if (brush->mins[type] < dist - PLANESIDE_EPSILON &&
 +					brush->maxs[type] > dist + PLANESIDE_EPSILON) s = PSIDE_BOTH;
 +	} //end if
 +
 +	if (s != PSIDE_BOTH)
 +#endif //ME
 +	{
 +		// if the brush actually uses the planenum,
 +		// we can tell the side for sure
 +		for (i = 0; i < brush->numsides; i++)
 +		{
 +			num = brush->sides[i].planenum;
 +			if (num >= MAX_MAPFILE_PLANES) Error ("bad planenum");
 +			if (num == planenum)
 +			{
 +				//we don't need to test this side plane again
 +				brush->sides[i].flags |= SFL_TESTED;
 +				return PSIDE_BACK|PSIDE_FACING;
 +			} //end if
 +			if (num == (planenum ^ 1) )
 +			{
 +				//we don't need to test this side plane again
 +				brush->sides[i].flags |= SFL_TESTED;
 +				return PSIDE_FRONT|PSIDE_FACING;
 +			} //end if
 +		} //end for
 +
 +		// box on plane side
 +		s = BoxOnPlaneSide (brush->mins, brush->maxs, plane);
 +
 +		if (s != PSIDE_BOTH) return s;
 +	} //end if
 +
 +	// if both sides, count the visible faces split
 +	d_front = d_back = 0;
 +
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		if (brush->sides[i].texinfo == TEXINFO_NODE)
 +			continue;		// on node, don't worry about splits
 +		if (!(brush->sides[i].flags & SFL_VISIBLE))
 +			continue;		// we don't care about non-visible
 +		w = brush->sides[i].winding;
 +		if (!w) continue;
 +		front = back = 0;
 +		for (j = 0; j < w->numpoints; j++)
 +		{
 +			d = DotProduct(w->p[j], plane->normal) - plane->dist;
 +			if (d > d_front) d_front = d;
 +			if (d < d_back) d_back = d;
 +			if (d > 0.1) // PLANESIDE_EPSILON)
 +				front = 1;
 +			if (d < -0.1) // PLANESIDE_EPSILON)
 +				back = 1;
 +		} //end for
 +		if (front && back)
 +		{
 +			if ( !(brush->sides[i].surf & SURF_SKIP) )
 +			{
 +				(*numsplits)++;
 +				if (brush->sides[i].surf & SURF_HINT)
 +				{
 +					*hintsplit = true;
 +				} //end if
 +			} //end if
 +		} //end if
 +	} //end for
 +
 +	if ( (d_front > 0.0 && d_front < 1.0)
 +		|| (d_back < 0.0 && d_back > -1.0) )
 +		(*epsilonbrush)++;
 +
 +#if 0
 +	if (*numsplits == 0)
 +	{	//	didn't really need to be split
 +		if (front) s = PSIDE_FRONT;
 +		else if (back) s = PSIDE_BACK;
 +		else s = 0;
 +	}
 +#endif
 +
 +	return s;
 +} //end of the function TestBrushToPlanenum
 +//===========================================================================
 +// Returns true if the winding would be crunched out of
 +// existance by the vertex snapping.
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +#define	EDGE_LENGTH	0.2
 +qboolean WindingIsTiny (winding_t *w)
 +{
 +#if 0
 +	if (WindingArea (w) < 1)
 +		return true;
 +	return false;
 +#else
 +	int		i, j;
 +	vec_t	len;
 +	vec3_t	delta;
 +	int		edges;
 +
 +	edges = 0;
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		j = i == w->numpoints - 1 ? 0 : i+1;
 +		VectorSubtract (w->p[j], w->p[i], delta);
 +		len = VectorLength (delta);
 +		if (len > EDGE_LENGTH)
 +		{
 +			if (++edges == 3)
 +				return false;
 +		}
 +	}
 +	return true;
 +#endif
 +} //end of the function WindingIsTiny
 +//===========================================================================
 +// Returns true if the winding still has one of the points
 +// from basewinding for plane
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WindingIsHuge (winding_t *w)
 +{
 +	int		i, j;
 +
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			if (w->p[i][j] < -BOGUS_RANGE+1 || w->p[i][j] > BOGUS_RANGE-1)
 +				return true;
 +	}
 +	return false;
 +} //end of the function WindingIsHuge
 +//===========================================================================
 +// creates a leaf out of the given nodes with the given brushes
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void LeafNode(node_t *node, bspbrush_t *brushes)
 +{
 +	bspbrush_t *b;
 +	int i;
 +
 +	node->side = NULL;
 +	node->planenum = PLANENUM_LEAF;
 +	node->contents = 0;
 +
 +	for (b = brushes; b; b = b->next)
 +	{
 +		// if the brush is solid and all of its sides are on nodes,
 +		// it eats everything
 +		if (b->original->contents & CONTENTS_SOLID)
 +		{
 +			for (i=0 ; i<b->numsides ; i++)
 +				if (b->sides[i].texinfo != TEXINFO_NODE)
 +					break;
 +			if (i == b->numsides)
 +			{
 +				node->contents = CONTENTS_SOLID;
 +				break;
 +			} //end if
 +		} //end if
 +		node->contents |= b->original->contents;
 +	} //end for
 +
 +	if (create_aas)
 +	{
 +		node->expansionbboxes = 0;
 +		node->contents = 0;
 +		for (b = brushes; b; b = b->next)
 +		{
 +			node->expansionbboxes |= b->original->expansionbbox;
 +			node->contents |= b->original->contents;
 +			if (b->original->modelnum)
 +				node->modelnum = b->original->modelnum;
 +		} //end for
 +		if (node->contents & CONTENTS_SOLID)
 +		{
 +			if (node->expansionbboxes != cfg.allpresencetypes)
 +			{
 +				node->contents &= ~CONTENTS_SOLID;
 +			} //end if
 +		} //end if
 +	} //end if
 +
 +	node->brushlist = brushes;
 +} //end of the function LeafNode
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void CheckPlaneAgainstParents (int pnum, node_t *node)
 +{
 +	node_t	*p;
 +
 +	for (p = node->parent; p; p = p->parent)
 +	{
 +		if (p->planenum == pnum) Error("Tried parent");
 +	} //end for
 +} //end of the function CheckPlaneAgainstParants
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
 +{
 +	bspbrush_t	*front, *back;
 +	qboolean	good;
 +
 +	SplitBrush (node->volume, pnum, &front, &back);
 +
 +	good = (front && back);
 +
 +	if (front) FreeBrush (front);
 +	if (back) FreeBrush (back);
 +
 +	return good;
 +} //end of the function CheckPlaneAgaintsVolume
 +//===========================================================================
 +// Using a hueristic, choses one of the sides out of the brushlist
 +// to partition the brushes with.
 +// Returns NULL if there are no valid planes to split with..
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
 +{
 +	int			value, bestvalue;
 +	bspbrush_t	*brush, *test;
 +	side_t		*side, *bestside;
 +	int			i, pass, numpasses;
 +	int			pnum;
 +	int			s;
 +	int			front, back, both, facing, splits;
 +	int			bsplits;
 +	int			bestsplits;
 +	int			epsilonbrush;
 +	qboolean	hintsplit = false;
 +
 +	bestside = NULL;
 +	bestvalue = -99999;
 +	bestsplits = 0;
 +
 +	// the search order goes: visible-structural, visible-detail,
 +	// nonvisible-structural, nonvisible-detail.
 +	// If any valid plane is available in a pass, no further
 +	// passes will be tried.
 +	numpasses = 2;
 +	for (pass = 0; pass < numpasses; pass++)
 +	{
 +		for (brush = brushes; brush; brush = brush->next)
 +		{
 +			// only check detail the second pass
 +//			if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) )
 +//				continue;
 +//			if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) )
 +//				continue;
 +			for (i = 0; i < brush->numsides; i++)
 +			{
 +				side = brush->sides + i;
 +//				if (side->flags & SFL_BEVEL)
 +//					continue;	// never use a bevel as a spliter
 +				if (!side->winding)
 +					continue;	// nothing visible, so it can't split
 +				if (side->texinfo == TEXINFO_NODE)
 +					continue;	// allready a node splitter
 +				if (side->flags & SFL_TESTED)
 +					continue;	// we allready have metrics for this plane
 +//				if (side->surf & SURF_SKIP)
 +//					continue;	// skip surfaces are never chosen
 +
 +//				if (!(side->flags & SFL_VISIBLE) && (pass < 2))
 +//					continue;	// only check visible faces on first pass
 +
 +				if ((side->flags & SFL_CURVE) && (pass < 1))
 +					continue;	// only check curves the second pass
 +
 +				pnum = side->planenum;
 +				pnum &= ~1;	// allways use positive facing plane
 +
 +				CheckPlaneAgainstParents (pnum, node);
 +
 +				if (!CheckPlaneAgainstVolume (pnum, node))
 +					continue;	// would produce a tiny volume
 +
 +				front = 0;
 +				back = 0;
 +				both = 0;
 +				facing = 0;
 +				splits = 0;
 +				epsilonbrush = 0;
 +
 +				 //inner loop: optimize
 +				for (test = brushes; test; test = test->next)
 +				{
 +					s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);
 +
 +					splits += bsplits;
 +//					if (bsplits && (s&PSIDE_FACING) )
 +//						Error ("PSIDE_FACING with splits");
 +
 +					test->testside = s;
 +					//
 +					if (s & PSIDE_FACING) facing++;
 +					if (s & PSIDE_FRONT) front++;
 +					if (s & PSIDE_BACK) back++;
 +					if (s == PSIDE_BOTH) both++;
 +				} //end for
 +
 +				// give a value estimate for using this plane
 +				value =  5*facing - 5*splits - abs(front-back);
 +//					value =  -5*splits;
 +//					value =  5*facing - 5*splits;
 +				if (mapplanes[pnum].type < 3)
 +					value+=5;		// axial is better
 +
 +				value -= epsilonbrush * 1000;	// avoid!
 +
 +				// never split a hint side except with another hint
 +				if (hintsplit && !(side->surf & SURF_HINT) )
 +					value = -9999999;
 +
 +				// save off the side test so we don't need
 +				// to recalculate it when we actually seperate
 +				// the brushes
 +				if (value > bestvalue)
 +				{
 +					bestvalue = value;
 +					bestside = side;
 +					bestsplits = splits;
 +					for (test = brushes; test ; test = test->next)
 +						test->side = test->testside;
 +				} //end if
 +			} //end for
 +		} //end for (brush = brushes;
 +
 +		// if we found a good plane, don't bother trying any
 +		// other passes
 +		if (bestside)
 +		{
 +			if (pass > 1)
 +			{
 +				if (numthreads == 1) c_nonvis++;
 +			}
 +			if (pass > 0) node->detail_seperator = true;	// not needed for vis
 +			break;
 +		} //end if
 +	} //end for (pass = 0;
 +
 +	//
 +	// clear all the tested flags we set
 +	//
 +	for (brush = brushes ; brush ; brush=brush->next)
 +	{
 +		for (i = 0; i < brush->numsides; i++)
 +		{
 +			brush->sides[i].flags &= ~SFL_TESTED;
 +		} //end for
 +	} //end for
 +
 +	return bestside;
 +} //end of the function SelectSplitSide
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
 +{
 +	int			i, j;
 +	winding_t	*w;
 +	vec_t		d, max;
 +	int			side;
 +
 +	max = 0;
 +	side = PSIDE_FRONT;
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (!w)
 +			continue;
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			d = DotProduct (w->p[j], plane->normal) - plane->dist;
 +			if (d > max)
 +			{
 +				max = d;
 +				side = PSIDE_FRONT;
 +			}
 +			if (-d > max)
 +			{
 +				max = -d;
 +				side = PSIDE_BACK;
 +			}
 +		}
 +	}
 +	return side;
 +} //end of the function BrushMostlyOnSide
 +//===========================================================================
 +// Generates two new brushes, leaving the original
 +// unchanged
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void SplitBrush (bspbrush_t *brush, int planenum,
 +	bspbrush_t **front, bspbrush_t **back)
 +{
 +	bspbrush_t	*b[2];
 +	int			i, j;
 +	winding_t	*w, *cw[2], *midwinding;
 +	plane_t		*plane, *plane2;
 +	side_t		*s, *cs;
 +	float d, d_front, d_back;
 +
 +	*front = *back = NULL;
 +	plane = &mapplanes[planenum];
 +
 +	// check all points
 +	d_front = d_back = 0;
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (!w)
 +			continue;
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			d = DotProduct (w->p[j], plane->normal) - plane->dist;
 +			if (d > 0 && d > d_front)
 +				d_front = d;
 +			if (d < 0 && d < d_back)
 +				d_back = d;
 +		}
 +	}
 +
 +	if (d_front < 0.2) // PLANESIDE_EPSILON)
 +	{	// only on back
 +		*back = CopyBrush (brush);
 +		return;
 +	}
 +	if (d_back > -0.2) // PLANESIDE_EPSILON)
 +	{	// only on front
 +		*front = CopyBrush (brush);
 +		return;
 +	}
 +
 +	// create a new winding from the split plane
 +
 +	w = BaseWindingForPlane (plane->normal, plane->dist);
 +	for (i=0 ; i<brush->numsides && w ; i++)
 +	{
 +		plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
 +		ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
 +	}
 +
 +	if (!w || WindingIsTiny(w))
 +	{	// the brush isn't really split
 +		int		side;
 +
 +		side = BrushMostlyOnSide (brush, plane);
 +		if (side == PSIDE_FRONT)
 +			*front = CopyBrush (brush);
 +		if (side == PSIDE_BACK)
 +			*back = CopyBrush (brush);
 +		//free a possible winding
 +		if (w) FreeWinding(w);
 +		return;
 +	}
 +
 +	if (WindingIsHuge (w))
 +	{
 +		Log_Write("WARNING: huge winding\n");
 +	}
 +
 +	midwinding = w;
 +
 +	// split it for real
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		b[i] = AllocBrush (brush->numsides+1);
 +		b[i]->original = brush->original;
 +	}
 +
 +	// split all the current windings
 +
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		s = &brush->sides[i];
 +		w = s->winding;
 +		if (!w)
 +			continue;
 +		ClipWindingEpsilon (w, plane->normal, plane->dist,
 +			0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
 +		for (j=0 ; j<2 ; j++)
 +		{
 +			if (!cw[j])
 +				continue;
 +#if 0
 +			if (WindingIsTiny (cw[j]))
 +			{
 +				FreeWinding (cw[j]);
 +				continue;
 +			}
 +#endif
 +			cs = &b[j]->sides[b[j]->numsides];
 +			b[j]->numsides++;
 +			*cs = *s;
 +//			cs->planenum = s->planenum;
 +//			cs->texinfo = s->texinfo;
 +//			cs->original = s->original;
 +			cs->winding = cw[j];
 +			cs->flags &= ~SFL_TESTED;
 +		}
 +	}
 +
 +
 +	// see if we have valid polygons on both sides
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		BoundBrush (b[i]);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			if (b[i]->mins[j] < -MAX_MAP_BOUNDS || b[i]->maxs[j] > MAX_MAP_BOUNDS)
 +			{
 +				Log_Write("bogus brush after clip");
 +				break;
 +			}
 +		}
 +
 +		if (b[i]->numsides < 3 || j < 3)
 +		{
 +			FreeBrush (b[i]);
 +			b[i] = NULL;
 +		}
 +	}
 +
 +	if ( !(b[0] && b[1]) )
 +	{
 +		if (!b[0] && !b[1])
 +			Log_Write("split removed brush\r\n");
 +		else
 +			Log_Write("split not on both sides\r\n");
 +		if (b[0])
 +		{
 +			FreeBrush (b[0]);
 +			*front = CopyBrush (brush);
 +		}
 +		if (b[1])
 +		{
 +			FreeBrush (b[1]);
 +			*back = CopyBrush (brush);
 +		}
 +		return;
 +	}
 +
 +	// add the midwinding to both sides
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		cs = &b[i]->sides[b[i]->numsides];
 +		b[i]->numsides++;
 +
 +		cs->planenum = planenum^i^1;
 +		cs->texinfo = TEXINFO_NODE; //never use these sides as splitters
 +		cs->flags &= ~SFL_VISIBLE;
 +		cs->flags &= ~SFL_TESTED;
 +		if (i==0)
 +			cs->winding = CopyWinding (midwinding);
 +		else
 +			cs->winding = midwinding;
 +	}
 +
 +{
 +	vec_t	v1;
 +	int i;
 +
 +	for (i = 0; i < 2; i++)
 +	{
 +		v1 = BrushVolume (b[i]);
 +		if (v1 < 1.0)
 +		{
 +			FreeBrush(b[i]);
 +			b[i] = NULL;
 +			//Log_Write("tiny volume after clip");
 +		}
 +	}
 +	if (!b[0] && !b[1])
 +	{
 +		Log_Write("two tiny brushes\r\n");
 +	} //end if
 +}
 +
 +	*front = b[0];
 +	*back = b[1];
 +} //end of the function SplitBrush
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void SplitBrushList (bspbrush_t *brushes, 
 +	node_t *node, bspbrush_t **front, bspbrush_t **back)
 +{
 +	bspbrush_t	*brush, *newbrush, *newbrush2;
 +	side_t		*side;
 +	int			sides;
 +	int			i;
 +
 +	*front = *back = NULL;
 +
 +	for (brush = brushes; brush; brush = brush->next)
 +	{
 +		sides = brush->side;
 +
 +		if (sides == PSIDE_BOTH)
 +		{	// split into two brushes
 +			SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
 +			if (newbrush)
 +			{
 +				newbrush->next = *front;
 +				*front = newbrush;
 +			} //end if
 +			if (newbrush2)
 +			{
 +				newbrush2->next = *back;
 +				*back = newbrush2;
 +			} //end if
 +			continue;
 +		} //end if
 +
 +		newbrush = CopyBrush (brush);
 +
 +		// if the planenum is actualy a part of the brush
 +		// find the plane and flag it as used so it won't be tried
 +		// as a splitter again
 +		if (sides & PSIDE_FACING)
 +		{
 +			for (i=0 ; i<newbrush->numsides ; i++)
 +			{
 +				side = newbrush->sides + i;
 +				if ( (side->planenum& ~1) == node->planenum)
 +					side->texinfo = TEXINFO_NODE;
 +			} //end for
 +		} //end if
 +		if (sides & PSIDE_FRONT)
 +		{
 +			newbrush->next = *front;
 +			*front = newbrush;
 +			continue;
 +		} //end if
 +		if (sides & PSIDE_BACK)
 +		{
 +			newbrush->next = *back;
 +			*back = newbrush;
 +			continue;
 +		} //end if
 +	} //end for
 +} //end of the function SplitBrushList
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void CheckBrushLists(bspbrush_t *brushlist1, bspbrush_t *brushlist2)
 +{
 +	bspbrush_t *brush1, *brush2;
 +
 +	for (brush1 = brushlist1; brush1; brush1 = brush1->next)
 +	{
 +		for (brush2 = brushlist2; brush2; brush2 = brush2->next)
 +		{
 +			assert(brush1 != brush2);
 +		} //end for
 +	} //end for
 +} //end of the function CheckBrushLists
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int numrecurse = 0;
 +
 +node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
 +{
 +	node_t		*newnode;
 +	side_t		*bestside;
 +	int			i, totalmem;
 +	bspbrush_t	*children[2];
 +
 +	qprintf("\r%6d", numrecurse);
 +	numrecurse++;
 +
 +	if (numthreads == 1)
 +	{
 +		totalmem = WindingMemory() + c_nodememory + c_brushmemory;
 +		if (totalmem > c_peak_totalbspmemory)
 +			c_peak_totalbspmemory = totalmem;
 +		c_nodes++;
 +	} //endif
 +
 +	if (drawflag)
 +		DrawBrushList(brushes, node);
 +
 +	// find the best plane to use as a splitter
 +	bestside = SelectSplitSide (brushes, node);
 +	if (!bestside)
 +	{
 +		// leaf node
 +		node->side = NULL;
 +		node->planenum = -1;
 +		LeafNode(node, brushes);
 +		if (node->contents & CONTENTS_SOLID) c_solidleafnodes++;
 +		if (create_aas)
 +		{
 +			//free up memory!!!
 +			FreeBrushList(node->brushlist);
 +			node->brushlist = NULL;
 +			//free the node volume brush
 +			if (node->volume)
 +			{
 +				FreeBrush(node->volume);
 +				node->volume = NULL;
 +			} //end if
 +		} //end if
 +		return node;
 +	} //end if
 +
 +	// this is a splitplane node
 +	node->side = bestside;
 +	node->planenum = bestside->planenum & ~1;	// always use front facing
 +
 +	//split the brush list in two for both children
 +	SplitBrushList (brushes, node, &children[0], &children[1]);
 +	//free the old brush list
 +	FreeBrushList (brushes);
 +
 +	// allocate children before recursing
 +	for (i = 0; i < 2; i++)
 +	{
 +		newnode = AllocNode ();
 +		newnode->parent = node;
 +		node->children[i] = newnode;
 +	} //end for
 +
 +	//split the volume brush of the node for the children
 +	SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
 +		&node->children[1]->volume);
 +
 +	if (create_aas)
 +	{
 +		//free the volume brush
 +		if (node->volume)
 +		{
 +			FreeBrush(node->volume);
 +			node->volume = NULL;
 +		} //end if
 +	} //end if
 +	// recursively process children
 +	for (i = 0; i < 2; i++)
 +	{
 +		node->children[i] = BuildTree_r(node->children[i], children[i]);
 +	} //end for
 +
 +	return node;
 +} //end of the function BuildTree_r
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +node_t *firstnode;		//first node in the list
 +node_t *lastnode;			//last node in the list
 +int nodelistsize;			//number of nodes in the list
 +int use_nodequeue = 0;	//use nodequeue, otherwise a node stack is used
 +int numwaiting = 0;
 +
 +void (*AddNodeToList)(node_t *node);
 +
 +//add the node to the front of the node list
 +//(effectively using a node stack)
 +void AddNodeToStack(node_t *node)
 +{
 +	ThreadLock();
 +
 +	node->next = firstnode;
 +	firstnode = node;
 +	if (!lastnode) lastnode = node;
 +	nodelistsize++;
 +
 +	ThreadUnlock();
 +	//
 +	ThreadSemaphoreIncrease(1);
 +} //end of the function AddNodeToStack
 +//add the node to the end of the node list
 +//(effectively using a node queue)
 +void AddNodeToQueue(node_t *node)
 +{
 +	ThreadLock();
 +
 +	node->next = NULL;
 +	if (lastnode) lastnode->next = node;
 +	else firstnode = node;
 +	lastnode = node;
 +	nodelistsize++;
 +
 +	ThreadUnlock();
 +	//
 +	ThreadSemaphoreIncrease(1);
 +} //end of the function AddNodeToQueue
 +//get the first node from the front of the node list
 +node_t *NextNodeFromList(void)
 +{
 +	node_t *node;
 +
 +	ThreadLock();
 +	numwaiting++;
 +	if (!firstnode)
 +	{
 +		if (numwaiting >= GetNumThreads()) ThreadSemaphoreIncrease(GetNumThreads());
 +	} //end if
 +	ThreadUnlock();
 +
 +	ThreadSemaphoreWait();
 +
 +	ThreadLock();
 +
 +	numwaiting--;
 +
 +	node = firstnode;
 +	if (firstnode)
 +	{
 +		firstnode = firstnode->next;
 +		nodelistsize--;
 +	} //end if
 +	if (!firstnode) lastnode = NULL;
 +
 +	ThreadUnlock();
 +
 +	return node;
 +} //end of the function NextNodeFromList
 +//returns the size of the node list
 +int NodeListSize(void)
 +{
 +	int size;
 +
 +	ThreadLock();
 +	size = nodelistsize;
 +	ThreadUnlock();
 +
 +	return size;
 +} //end of the function NodeListSize
 +//
 +void IncreaseNodeCounter(void)
 +{
 +	ThreadLock();
 +	//if (verbose) printf("\r%6d", numrecurse++);
 +	qprintf("\r%6d", numrecurse++);
 +	//qprintf("\r%6d %d, %5d ", numrecurse++, GetNumThreads(), nodelistsize);
 +	ThreadUnlock();
 +} //end of the function IncreaseNodeCounter
 +//thread function, gets nodes from the nodelist and processes them
 +void BuildTreeThread(int threadid)
 +{
 +	node_t *newnode, *node;
 +	side_t *bestside;
 +	int i, totalmem;
 +	bspbrush_t *brushes;
 +
 +	for (node = NextNodeFromList(); node; )
 +	{
 +		//if the nodelist isn't empty try to add another thread
 +		//if (NodeListSize() > 10) AddThread(BuildTreeThread);
 +		//display the number of nodes processed so far
 +		if (numthreads == 1)
 +			IncreaseNodeCounter();
 +
 +		brushes = node->brushlist;
 +
 +		if (numthreads == 1)
 +		{
 +			totalmem = WindingMemory() + c_nodememory + c_brushmemory;
 +			if (totalmem > c_peak_totalbspmemory)
 +			{
 +				c_peak_totalbspmemory = totalmem;
 +			} //end if
 +			c_nodes++;
 +		} //endif
 +
 +		if (drawflag)
 +		{
 +			DrawBrushList(brushes, node);
 +		} //end if
 +
 +		if (cancelconversion)
 +		{
 +			bestside = NULL;
 +		} //end if
 +		else
 +		{
 +			// find the best plane to use as a splitter
 +			bestside = SelectSplitSide(brushes, node);
 +		} //end else
 +		//if there's no split side left
 +		if (!bestside)
 +		{
 +			//create a leaf out of the node
 +			LeafNode(node, brushes);
 +			if (node->contents & CONTENTS_SOLID) c_solidleafnodes++;
 +			if (create_aas)
 +			{
 +				//free up memory!!!
 +				FreeBrushList(node->brushlist);
 +				node->brushlist = NULL;
 +			} //end if
 +			//free the node volume brush (it is not used anymore)
 +			if (node->volume)
 +			{
 +				FreeBrush(node->volume);
 +				node->volume = NULL;
 +			} //end if
 +			node = NextNodeFromList();
 +			continue;
 +		} //end if
 +
 +		// this is a splitplane node
 +		node->side = bestside;
 +		node->planenum = bestside->planenum & ~1;	//always use front facing
 +
 +		//allocate children
 +		for (i = 0; i < 2; i++)
 +		{
 +			newnode = AllocNode();
 +			newnode->parent = node;
 +			node->children[i] = newnode;
 +		} //end for
 +
 +		//split the brush list in two for both children
 +		SplitBrushList(brushes, node, &node->children[0]->brushlist, &node->children[1]->brushlist);
 +
 +		CheckBrushLists(node->children[0]->brushlist, node->children[1]->brushlist);
 +		//free the old brush list
 +		FreeBrushList(brushes);
 +		node->brushlist = NULL;
 +
 +		//split the volume brush of the node for the children
 +		SplitBrush(node->volume, node->planenum, &node->children[0]->volume,
 +								&node->children[1]->volume);
 +
 +		if (!node->children[0]->volume || !node->children[1]->volume)
 +		{
 +			Error("child without volume brush");
 +		} //end if
 +
 +		//free the volume brush
 +		if (node->volume)
 +		{
 +			FreeBrush(node->volume);
 +			node->volume = NULL;
 +		} //end if
 +		//add both children to the node list
 +		//AddNodeToList(node->children[0]);
 +		AddNodeToList(node->children[1]);
 +		node = node->children[0];
 +	} //end while
 +	RemoveThread(threadid);
 +} //end of the function BuildTreeThread
 +//===========================================================================
 +// build the bsp tree using a node list
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void BuildTree(tree_t *tree)
 +{
 +	int i;
 +
 +	firstnode = NULL;
 +	lastnode = NULL;
 +	//use a node queue or node stack
 +	if (use_nodequeue) AddNodeToList = AddNodeToQueue;
 +	else AddNodeToList = AddNodeToStack;
 +	//setup thread locking
 +	ThreadSetupLock();
 +	ThreadSetupSemaphore();
 +	numwaiting = 0;
 +	//
 +	Log_Print("%6d threads max\n", numthreads);
 +	if (use_nodequeue) Log_Print("breadth first bsp building\n");
 +	else Log_Print("depth first bsp building\n");
 +	qprintf("%6d splits", 0);
 +	//add the first node to the list
 +	AddNodeToList(tree->headnode);
 +	//start the threads
 +	for (i = 0; i < numthreads; i++)
 +		AddThread(BuildTreeThread);
 +	//wait for all added threads to be finished
 +	WaitForAllThreadsFinished();
 +	//shutdown the thread locking
 +	ThreadShutdownLock();
 +	ThreadShutdownSemaphore();
 +} //end of the function BuildTree
 +//===========================================================================
 +// The incoming brush list will be freed before exiting
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs)
 +{
 +	int i, c_faces, c_nonvisfaces, c_brushes;
 +	bspbrush_t *b;
 +	node_t *node;
 +	tree_t *tree;
 +	vec_t volume;
 +//	vec3_t point;
 +
 +	Log_Print("-------- Brush BSP ---------\n");
 +
 +	tree = Tree_Alloc();
 +
 +	c_faces = 0;
 +	c_nonvisfaces = 0;
 +	c_brushes = 0;
 +	c_totalsides = 0;
 +	for (b = brushlist; b; b = b->next)
 +	{
 +		c_brushes++;
 +
 +		volume = BrushVolume(b);
 +		if (volume < microvolume)
 +		{
 +			Log_Print("WARNING: entity %i, brush %i: microbrush\n",
 +				b->original->entitynum, b->original->brushnum);
 +		} //end if
 +
 +		for (i=0 ; i<b->numsides ; i++)
 +		{
 +			if (b->sides[i].flags & SFL_BEVEL)
 +				continue;
 +			if (!b->sides[i].winding)
 +				continue;
 +			if (b->sides[i].texinfo == TEXINFO_NODE)
 +				continue;
 +			if (b->sides[i].flags & SFL_VISIBLE)
 +			{
 +				c_faces++;
 +			} //end if
 +			else
 +			{
 +				c_nonvisfaces++;
 +				//if (create_aas) b->sides[i].texinfo = TEXINFO_NODE;
 +			} //end if
 +		} //end for
 +		c_totalsides += b->numsides;
 +
 +		AddPointToBounds (b->mins, tree->mins, tree->maxs);
 +		AddPointToBounds (b->maxs, tree->mins, tree->maxs);
 +	} //end for
 +
 +	Log_Print("%6i brushes\n", c_brushes);
 +	Log_Print("%6i visible faces\n", c_faces);
 +	Log_Print("%6i nonvisible faces\n", c_nonvisfaces);
 +	Log_Print("%6i total sides\n", c_totalsides);
 +
 +	c_active_brushes = c_brushes;
 +	c_nodememory = 0;
 +	c_brushmemory = 0;
 +	c_peak_brushmemory = 0;
 +
 +	c_nodes = 0;
 +	c_nonvis = 0;
 +	node = AllocNode ();
 +
 +	//volume of first node (head node)
 +	node->volume = BrushFromBounds (mins, maxs);
 +	//
 +	tree->headnode = node;
 +	//just get some statistics and the mins/maxs of the node
 +	numrecurse = 0;
 +//	qprintf("%6d splits", numrecurse);
 +
 +	tree->headnode->brushlist = brushlist;
 +	BuildTree(tree);
 +
 +	//build the bsp tree with the start node from the brushlist
 +//	node = BuildTree_r(node, brushlist);
 +
 +	//if the conversion is cancelled
 +	if (cancelconversion) return tree;
 +
 +	qprintf("\n");
 +	Log_Write("%6d splits\r\n", numrecurse);
 +//	Log_Print("%6i visible nodes\n", c_nodes/2 - c_nonvis);
 +//	Log_Print("%6i nonvis nodes\n", c_nonvis);
 +//	Log_Print("%6i leaves\n", (c_nodes+1)/2);
 +//	Log_Print("%6i solid leaf nodes\n", c_solidleafnodes);
 +//	Log_Print("%6i active brushes\n", c_active_brushes);
 +	if (numthreads == 1)
 +	{
 +//		Log_Print("%6i KB of node memory\n", c_nodememory >> 10);
 +//		Log_Print("%6i KB of brush memory\n", c_brushmemory >> 10);
 +//		Log_Print("%6i KB of peak brush memory\n", c_peak_brushmemory >> 10);
 +//		Log_Print("%6i KB of winding memory\n", WindingMemory() >> 10);
 +//		Log_Print("%6i KB of peak winding memory\n", WindingPeakMemory() >> 10);
 +		Log_Print("%6i KB of peak total bsp memory\n", c_peak_totalbspmemory >> 10);
 +	} //end if
 +
 +	/*
 +	point[0] = 1485;
 +	point[1] = 956.125;
 +	point[2] = 352.125;
 +	node = PointInLeaf(tree->headnode, point);
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		Log_Print("node not a leaf\n");
 +	} //end if
 +	Log_Print("at %f %f %f:\n", point[0], point[1], point[2]);
 +	PrintContents(node->contents);
 +	Log_Print("node->expansionbboxes = %d\n", node->expansionbboxes);
 +	//*/
 +	return tree;
 +} //end of the function BrushBSP
 +
 diff --git a/code/bspc/bspc.c b/code/bspc/bspc.c new file mode 100755 index 0000000..4f58b53 --- /dev/null +++ b/code/bspc/bspc.c @@ -0,0 +1,991 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#if defined(WIN32) || defined(_WIN32)
 +#include <direct.h>
 +#include <windows.h>
 +#include <sys/types.h>
 +#include <sys/stat.h>
 +#else
 +#include <unistd.h>
 +#include <glob.h>
 +#include <sys/stat.h>
 +#include <unistd.h>
 +#endif
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"
 +#include "../botlib/be_aas_cluster.h"
 +#include "../botlib/be_aas_optimize.h"
 +#include "aas_create.h"
 +#include "aas_store.h"
 +#include "aas_file.h"
 +#include "aas_cfg.h"
 +#include "be_aas_bspc.h"
 +
 +extern	int use_nodequeue;		//brushbsp.c
 +extern	int calcgrapplereach;	//be_aas_reach.c
 +
 +float			subdivide_size = 240;
 +char			source[1024];
 +char			name[1024];
 +vec_t			microvolume = 1.0;
 +char			outbase[32];
 +int				entity_num;
 +aas_settings_t	aassettings;
 +
 +qboolean	noprune;			//don't prune nodes (bspc.c)
 +qboolean	glview;				//create a gl view
 +qboolean	nodetail;			//don't use detail brushes (map.c)
 +qboolean	fulldetail;			//use but don't mark detail brushes (map.c)
 +qboolean	onlyents;			//only process the entities (bspc.c)
 +qboolean	nomerge;			//don't merge bsp node faces (faces.c)
 +qboolean	nowater;			//don't use the water brushes (map.c)
 +qboolean	nocsg;				//don't carve intersecting brushes (bspc.c)
 +qboolean	noweld;				//use unique face vertexes (faces.c)
 +qboolean	noshare;			//don't share bsp edges (faces.c)
 +qboolean	nosubdiv;			//don't subdivide bsp node faces (faces.c)
 +qboolean	notjunc;			//don't create tjunctions (edge melting) (faces.c)
 +qboolean	optimize;			//enable optimisation
 +qboolean	leaktest;			//perform a leak test
 +qboolean	verboseentities;
 +qboolean	freetree;			//free the bsp tree when not needed anymore
 +qboolean	create_aas;			//create an .AAS file
 +qboolean	nobrushmerge;		//don't merge brushes
 +qboolean	lessbrushes;		//create less brushes instead of correct texture placement
 +qboolean	cancelconversion;	//true if the conversion is being cancelled
 +qboolean	noliquids;			//no liquids when writing map file
 +qboolean	forcesidesvisible;	//force all brush sides to be visible when loaded from bsp
 +qboolean	capsule_collision = 0;
 +
 +/*
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ProcessWorldModel (void)
 +{
 +	entity_t	*e;
 +	tree_t *tree;
 +	qboolean	leaked;
 +	int brush_start, brush_end;
 +
 +	e = &entities[entity_num];
 +
 +	brush_start = e->firstbrush;
 +	brush_end = brush_start + e->numbrushes;
 +	leaked = false;
 +
 +	//process the whole world in one time
 +	tree = ProcessWorldBrushes(brush_start, brush_end);
 +	//create the bsp tree portals
 +	MakeTreePortals(tree);
 +	//mark all leafs that can be reached by entities
 +	if (FloodEntities(tree))
 +	{
 +		FillOutside(tree->headnode);
 +	} //end if
 +	else
 +	{
 +		Log_Print("**** leaked ****\n");
 +		leaked = true;
 +		LeakFile(tree);
 +		if (leaktest)
 +		{
 +			Log_Print("--- MAP LEAKED ---\n");
 +			exit(0);
 +		} //end if
 +	} //end else
 +
 +	MarkVisibleSides (tree, brush_start, brush_end);
 +
 +	FloodAreas (tree);
 +
 +#ifndef ME
 +	if (glview) WriteGLView(tree, source);
 +#endif
 +	MakeFaces(tree->headnode);
 +	FixTjuncs(tree->headnode);
 +
 +	//NOTE: Never prune the nodes because the portals
 +	//		are screwed when prunning is done and as
 +	//		a result portal writing will crash
 +	//if (!noprune) PruneNodes(tree->headnode);
 +
 +	WriteBSP(tree->headnode);
 +
 +	if (!leaked) WritePortalFile(tree);
 +
 +	Tree_Free(tree);
 +} //end of the function ProcessWorldModel
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ProcessSubModel (void)
 +{
 +	entity_t	*e;
 +	int start, end;
 +	tree_t *tree;
 +	bspbrush_t *list;
 +	vec3_t mins, maxs;
 +
 +	e = &entities[entity_num];
 +
 +	start = e->firstbrush;
 +	end = start + e->numbrushes;
 +
 +	mins[0] = mins[1] = mins[2] = -4096;
 +	maxs[0] = maxs[1] = maxs[2] = 4096;
 +	list = MakeBspBrushList(start, end, mins, maxs);
 +	if (!nocsg) list = ChopBrushes (list);
 +	tree = BrushBSP (list, mins, maxs);
 +	MakeTreePortals (tree);
 +	MarkVisibleSides (tree, start, end);
 +	MakeFaces (tree->headnode);
 +	FixTjuncs (tree->headnode);
 +	WriteBSP (tree->headnode);
 +	Tree_Free(tree);
 +} //end of the function ProcessSubModel
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ProcessModels (void)
 +{
 +	BeginBSPFile();
 +
 +	for (entity_num = 0; entity_num < num_entities; entity_num++)
 +	{
 +		if (!entities[entity_num].numbrushes)
 +			continue;
 +
 +		Log_Print("############### model %i ###############\n", nummodels);
 +		BeginModel();
 +		if (entity_num == 0) ProcessWorldModel();
 +		else ProcessSubModel();
 +		EndModel();
 +
 +		if (!verboseentities)
 +			verbose = false;	// don't bother printing submodels
 +	} //end for
 +	EndBSPFile();
 +} //end of the function ProcessModels
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Win_Map2Bsp(char *bspfilename)
 +{
 +	double start, end;
 +	char path[1024];
 +
 +	start = I_FloatTime();
 +
 +	ThreadSetDefault();
 +	//yeah sure Carmack
 +	//numthreads = 1;		// multiple threads aren't helping...
 +
 +	strcpy(source, ExpandArg(bspfilename));
 +	StripExtension(source);
 +
 +	//delete portal and line files
 +	sprintf(path, "%s.prt", source);
 +	remove(path);
 +	sprintf(path, "%s.lin", source);
 +	remove(path);
 +
 +	strcpy(name, ExpandArg(bspfilename));	
 +	DefaultExtension(name, ".map");	// might be .reg
 +
 +	Q2_AllocMaxBSP();
 +	//
 +	SetModelNumbers();
 +	SetLightStyles();
 +	ProcessModels();
 +	//write the BSP
 +	Q2_WriteBSPFile(bspfilename);
 +
 +	Q2_FreeMaxBSP();
 +
 +	end = I_FloatTime();
 +	Log_Print("%5.0f seconds elapsed\n", end-start);
 +} //end of the function Win_Map2Bsp
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Map2Bsp(char *mapfilename, char *outputfilename)
 +{
 +	double start, end;
 +	char path[1024];
 +
 +	start = I_FloatTime ();
 +
 +	ThreadSetDefault ();
 +	//yeah sure Carmack
 +	//numthreads = 1;		//multiple threads aren't helping...
 +	//SetQdirFromPath(bspfilename);
 +
 +	strcpy(source, ExpandArg(mapfilename));
 +	StripExtension(source);
 +
 +	// delete portal and line files
 +	sprintf(path, "%s.prt", source);
 +	remove(path);
 +	sprintf(path, "%s.lin", source);
 +	remove(path);
 +
 +	strcpy(name, ExpandArg(mapfilename));
 +	DefaultExtension(name, ".map");	// might be .reg
 +
 +	//
 +	// if onlyents, just grab the entites and resave
 +	//
 +	if (onlyents)
 +	{
 +		char out[1024];
 +
 +		Q2_AllocMaxBSP();
 +		sprintf (out, "%s.bsp", source);
 +		Q2_LoadBSPFile(out, 0, 0);
 +		num_entities = 0;
 +
 +		Q2_LoadMapFile(name);
 +		SetModelNumbers();
 +		SetLightStyles();
 +
 +		Q2_UnparseEntities();
 +
 +		Q2_WriteBSPFile(out);
 +		//
 +		Q2_FreeMaxBSP();
 +	} //end if
 +	else
 +	{
 +		//
 +		// start from scratch
 +		//
 +		Q2_AllocMaxBSP();
 +		//load the map
 +		Q2_LoadMapFile(name);
 +		//create the .bsp file
 +		SetModelNumbers();
 +		SetLightStyles();
 +		ProcessModels();
 +		//write the BSP
 +		Q2_WriteBSPFile(outputfilename);
 +		//
 +		Q2_FreeMaxBSP();
 +	} //end else
 +
 +	end = I_FloatTime();
 +	Log_Print("%5.0f seconds elapsed\n", end-start);
 +} //end of the function Map2Bsp
 +*/
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void AASOuputFile(quakefile_t *qf, char *outputpath, char *filename)
 +{
 +	char ext[MAX_PATH];
 +
 +	//
 +	if (strlen(outputpath))
 +	{
 +		strcpy(filename, outputpath);
 +		//append the bsp file base
 +		AppendPathSeperator(filename, MAX_PATH);
 +		ExtractFileBase(qf->origname, &filename[strlen(filename)]);
 +		//append .aas
 +		strcat(filename, ".aas");
 +		return;
 +	} //end if
 +	//
 +	ExtractFileExtension(qf->filename, ext);
 +	if (!stricmp(ext, "pk3") || !stricmp(ext, "pak") || !stricmp(ext, "sin"))
 +	{
 +		strcpy(filename, qf->filename);
 +		while(strlen(filename) &&
 +				filename[strlen(filename)-1] != '\\' &&
 +				filename[strlen(filename)-1] != '/')
 +		{
 +			filename[strlen(filename)-1] = '\0';
 +		} //end while
 +		strcat(filename, "maps");
 +		if (access(filename, 0x04)) CreatePath(filename);
 +		//append the bsp file base
 +		AppendPathSeperator(filename, MAX_PATH);
 +		ExtractFileBase(qf->origname, &filename[strlen(filename)]);
 +		//append .aas
 +		strcat(filename, ".aas");
 +	} //end if
 +	else
 +	{
 +		strcpy(filename, qf->filename);
 +		while(strlen(filename) &&
 +				filename[strlen(filename)-1] != '.')
 +		{
 +			filename[strlen(filename)-1] = '\0';
 +		} //end while
 +		strcat(filename, "aas");
 +	} //end else
 +} //end of the function AASOutputFile
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void CreateAASFilesForAllBSPFiles(char *quakepath)
 +{
 +#if defined(WIN32)|defined(_WIN32)
 +	WIN32_FIND_DATA filedata;
 +	HWND handle;
 +	struct _stat statbuf;
 +#else
 +	glob_t globbuf;
 +	struct stat statbuf;
 +	int j;
 +#endif
 +	int done;
 +	char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH];
 +	char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH];
 +	quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles;
 +
 +	strcpy(filter, quakepath);
 +	AppendPathSeperator(filter, sizeof(filter));
 +	strcat(filter, "*");
 +
 +#if defined(WIN32)|defined(_WIN32)
 +	handle = FindFirstFile(filter, &filedata);
 +	done = (handle == INVALID_HANDLE_VALUE);
 +	while(!done)
 +	{
 +		_splitpath(filter, foldername, NULL, NULL, NULL);
 +		_splitpath(filter, NULL, &foldername[strlen(foldername)], NULL, NULL);
 +		AppendPathSeperator(foldername, _MAX_PATH);
 +		strcat(foldername, filedata.cFileName);
 +		_stat(foldername, &statbuf);
 +#else
 +	glob(filter, 0, NULL, &globbuf);
 +	for (j = 0; j < globbuf.gl_pathc; j++)
 +	{
 +		strcpy(foldername, globbuf.gl_pathv[j]);
 +		stat(foldername, &statbuf);
 +#endif
 +		//if it is a folder
 +		if (statbuf.st_mode & S_IFDIR)
 +		{
 +			//
 +			AppendPathSeperator(foldername, sizeof(foldername));
 +			//get all the bsp files
 +			strcpy(bspfilter, foldername);
 +			strcat(bspfilter, "maps/*.bsp");
 +			files = FindQuakeFiles(bspfilter);
 +			strcpy(bspfilter, foldername);
 +			strcat(bspfilter, "*.pk3/maps/*.bsp");
 +			bspfiles = FindQuakeFiles(bspfilter);
 +			for (qf = bspfiles; qf; qf = qf->next) if (!qf->next) break;
 +			if (qf) qf->next = files;
 +			else bspfiles = files;
 +			//get all the aas files
 +			strcpy(aasfilter, foldername);
 +			strcat(aasfilter, "maps/*.aas");
 +			files = FindQuakeFiles(aasfilter);
 +			strcpy(aasfilter, foldername);
 +			strcat(aasfilter, "*.pk3/maps/*.aas");
 +			aasfiles = FindQuakeFiles(aasfilter);
 +			for (qf = aasfiles; qf; qf = qf->next) if (!qf->next) break;
 +			if (qf) qf->next = files;
 +			else aasfiles = files;
 +			//
 +			for (qf = bspfiles; qf; qf = qf->next)
 +			{
 +				sprintf(aasfile, "%s/%s", qf->pakfile, qf->origname);
 +				Log_Print("found %s\n", aasfile);
 +				strcpy(&aasfile[strlen(aasfile)-strlen(".bsp")], ".aas");
 +				for (qf2 = aasfiles; qf2; qf2 = qf2->next)
 +				{
 +					sprintf(buf, "%s/%s", qf2->pakfile, qf2->origname);
 +					if (!stricmp(aasfile, buf))
 +					{
 +						Log_Print("found %s\n", buf);
 +						break;
 +					} //end if
 +				} //end for
 +			} //end for
 +		} //end if
 +#if defined(WIN32)|defined(_WIN32)
 +		//find the next file
 +		done = !FindNextFile(handle, &filedata);
 +	} //end while
 +#else
 +	} //end for
 +	globfree(&globbuf);
 +#endif
 +} //end of the function CreateAASFilesForAllBSPFiles
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +quakefile_t *GetArgumentFiles(int argc, char *argv[], int *i, char *ext)
 +{
 +	quakefile_t *qfiles, *lastqf, *qf;
 +	int j;
 +	char buf[1024];
 +
 +	qfiles = NULL;
 +	lastqf = NULL;
 +	for (; (*i)+1 < argc && argv[(*i)+1][0] != '-'; (*i)++)
 +	{
 +		strcpy(buf, argv[(*i)+1]);
 +		for (j = strlen(buf)-1; j >= strlen(buf)-4; j--)
 +			if (buf[j] == '.') break;
 +		if (j >= strlen(buf)-4)
 +			strcpy(&buf[j+1], ext);
 +		qf = FindQuakeFiles(buf);
 +		if (!qf) continue;
 +		if (lastqf) lastqf->next = qf;
 +		else qfiles = qf;
 +		lastqf = qf;
 +		while(lastqf->next) lastqf = lastqf->next;
 +	} //end for
 +	return qfiles;
 +} //end of the function GetArgumentFiles
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +
 +#define COMP_BSP2MAP		1
 +#define COMP_BSP2AAS		2
 +#define COMP_REACH			3
 +#define COMP_CLUSTER		4
 +#define COMP_AASOPTIMIZE	5
 +#define COMP_AASINFO		6
 +
 +int main (int argc, char **argv)
 +{
 +	int i, comp = 0;
 +	char outputpath[MAX_PATH] = "";
 +	char filename[MAX_PATH] = "unknown";
 +	quakefile_t *qfiles, *qf;
 +	double start_time;
 +
 +	myargc = argc;
 +	myargv = argv;
 +
 +	start_time = I_FloatTime();
 +
 +	Log_Open("bspc.log");		//open a log file
 +	Log_Print("BSPC version "BSPC_VERSION", %s %s\n", __DATE__, __TIME__);
 +
 +	DefaultCfg();
 +	for (i = 1; i < argc; i++)
 +	{
 +		if (!stricmp(argv[i],"-threads"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			numthreads = atoi(argv[++i]);
 +			Log_Print("threads = %d\n", numthreads);
 +		} //end if
 +		else if (!stricmp(argv[i], "-noverbose"))
 +		{
 +			Log_Print("verbose = false\n");
 +			verbose = false;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-nocsg"))
 +		{
 +			Log_Print("nocsg = true\n");
 +			nocsg = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-optimize"))
 +		{
 +			Log_Print("optimize = true\n");
 +			optimize = true;
 +		} //end else if
 +		/*
 +		else if (!stricmp(argv[i],"-glview"))
 +		{
 +			glview = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-draw"))
 +		{
 +			Log_Print("drawflag = true\n");
 +			drawflag = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-noweld"))
 +		{
 +			Log_Print("noweld = true\n");
 +			noweld = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-noshare"))
 +		{
 +			Log_Print("noshare = true\n");
 +			noshare = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-notjunc"))
 +		{
 +			Log_Print("notjunc = true\n");
 +			notjunc = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-nowater"))
 +		{
 +			Log_Print("nowater = true\n");
 +			nowater = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-noprune"))
 +		{
 +			Log_Print("noprune = true\n");
 +			noprune = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-nomerge"))
 +		{
 +			Log_Print("nomerge = true\n");
 +			nomerge = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-nosubdiv"))
 +		{
 +			Log_Print("nosubdiv = true\n");
 +			nosubdiv = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-nodetail"))
 +		{
 +			Log_Print("nodetail = true\n");
 +			nodetail = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-fulldetail"))
 +		{
 +			Log_Print("fulldetail = true\n");
 +			fulldetail = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-onlyents"))
 +		{
 +			Log_Print("onlyents = true\n");
 +			onlyents = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-micro"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			microvolume = atof(argv[++i]);
 +			Log_Print("microvolume = %f\n", microvolume);
 +		} //end else if
 +		else if (!stricmp(argv[i], "-leaktest"))
 +		{
 +			Log_Print("leaktest = true\n");
 +			leaktest = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-verboseentities"))
 +		{
 +			Log_Print("verboseentities = true\n");
 +			verboseentities = true;
 +		} //end else if
 +		else if (!stricmp(argv[i], "-chop"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			subdivide_size = atof(argv[++i]);
 +			Log_Print("subdivide_size = %f\n", subdivide_size);
 +		} //end else if
 +		else if (!stricmp (argv[i], "-tmpout"))
 +		{
 +			strcpy (outbase, "/tmp");
 +			Log_Print("temp output\n");
 +		} //end else if
 +		*/
 +#ifdef ME
 +		else if (!stricmp(argv[i], "-freetree"))
 +		{
 +			freetree = true;
 +			Log_Print("freetree = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-grapplereach"))
 +		{
 +			calcgrapplereach = true;
 +			Log_Print("grapplereach = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-nobrushmerge"))
 +		{
 +			nobrushmerge = true;
 +			Log_Print("nobrushmerge = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-noliquids"))
 +		{
 +			noliquids = true;
 +			Log_Print("noliquids = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-forcesidesvisible"))
 +		{
 +			forcesidesvisible = true;
 +			Log_Print("forcesidesvisible = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-output"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			if (access(argv[i+1], 0x04)) Warning("the folder %s does not exist", argv[i+1]);
 +			strcpy(outputpath, argv[++i]);
 +		} //end else if
 +		else if (!stricmp(argv[i], "-breadthfirst"))
 +		{
 +			use_nodequeue = true;
 +			Log_Print("breadthfirst = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-capsule"))
 +		{
 +			capsule_collision = true;
 +			Log_Print("capsule_collision = true\n");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-cfg"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			if (!LoadCfgFile(argv[++i]))
 +				exit(0);
 +		} //end else if
 +		else if (!stricmp(argv[i], "-bsp2map"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			comp = COMP_BSP2MAP;
 +			qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-bsp2aas"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			comp = COMP_BSP2AAS;
 +			qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-aasall"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			CreateAASFilesForAllBSPFiles(argv[++i]);
 +		} //end else if
 +		else if (!stricmp(argv[i], "-reach"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			comp = COMP_REACH;
 +			qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-cluster"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			comp = COMP_CLUSTER;
 +			qfiles = GetArgumentFiles(argc, argv, &i, "bsp");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-aasinfo"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			comp = COMP_AASINFO;
 +			qfiles = GetArgumentFiles(argc, argv, &i, "aas");
 +		} //end else if
 +		else if (!stricmp(argv[i], "-aasopt"))
 +		{
 +			if (i + 1 >= argc) {i = 0; break;}
 +			comp = COMP_AASOPTIMIZE;
 +			qfiles = GetArgumentFiles(argc, argv, &i, "aas");
 +		} //end else if
 +#endif //ME
 +		else
 +		{
 +			Log_Print("unknown parameter %s\n", argv[i]);
 +			break;
 +		} //end else
 +	} //end for
 +
 +	//if there are parameters and there's no mismatch in one of the parameters
 +	if (argc > 1 && i == argc)
 +	{
 +		switch(comp)
 +		{
 +			case COMP_BSP2MAP:
 +			{
 +				if (!qfiles) Log_Print("no files found\n");
 +				for (qf = qfiles; qf; qf = qf->next)
 +				{
 +					//copy the output path
 +					strcpy(filename, outputpath);
 +					//append the bsp file base
 +					AppendPathSeperator(filename, MAX_PATH);
 +					ExtractFileBase(qf->origname, &filename[strlen(filename)]);
 +					//append .map
 +					strcat(filename, ".map");
 +					//
 +					Log_Print("bsp2map: %s to %s\n", qf->origname, filename);
 +					if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
 +					//
 +					LoadMapFromBSP(qf);
 +					//write the map file
 +					WriteMapFile(filename);
 +				} //end for
 +				break;
 +			} //end case
 +			case COMP_BSP2AAS:
 +			{
 +				if (!qfiles) Log_Print("no files found\n");
 +				for (qf = qfiles; qf; qf = qf->next)
 +				{
 +					AASOuputFile(qf, outputpath, filename);
 +					//
 +					Log_Print("bsp2aas: %s to %s\n", qf->origname, filename);
 +					if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
 +					//set before map loading
 +					create_aas = 1;
 +					LoadMapFromBSP(qf);
 +					//create the AAS file
 +					AAS_Create(filename);
 +					//if it's a Quake3 map calculate the reachabilities and clusters
 +					if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf);
 +					//
 +					if (optimize) AAS_Optimize();
 +					//
 +					//write out the stored AAS file
 +					if (!AAS_WriteAASFile(filename))
 +					{
 +						Error("error writing %s\n", filename);
 +					} //end if
 +					//deallocate memory
 +					AAS_FreeMaxAAS();
 +				} //end for
 +				break;
 +			} //end case
 +			case COMP_REACH:
 +			{
 +				if (!qfiles) Log_Print("no files found\n");
 +				for (qf = qfiles; qf; qf = qf->next)
 +				{
 +					AASOuputFile(qf, outputpath, filename);
 +					//
 +					Log_Print("reach: %s to %s\n", qf->origname, filename);
 +					if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
 +					//if the AAS file exists in the output directory
 +					if (!access(filename, 0x04))
 +					{
 +						if (!AAS_LoadAASFile(filename, 0, 0))
 +						{
 +							Error("error loading aas file %s\n", filename);
 +						} //end if
 +						//assume it's a Quake3 BSP file
 +						loadedmaptype = MAPTYPE_QUAKE3;
 +					} //end if
 +					else
 +					{
 +						Warning("AAS file %s not found in output folder\n", filename);
 +						Log_Print("creating %s...\n", filename);
 +						//set before map loading
 +						create_aas = 1;
 +						LoadMapFromBSP(qf);
 +						//create the AAS file
 +						AAS_Create(filename);
 +					} //end else
 +					//if it's a Quake3 map calculate the reachabilities and clusters
 +					if (loadedmaptype == MAPTYPE_QUAKE3)
 +					{
 +						AAS_CalcReachAndClusters(qf);
 +					} //end if
 +					//
 +					if (optimize) AAS_Optimize();
 +					//write out the stored AAS file
 +					if (!AAS_WriteAASFile(filename))
 +					{
 +						Error("error writing %s\n", filename);
 +					} //end if
 +					//deallocate memory
 +					AAS_FreeMaxAAS();
 +				} //end for
 +				break;
 +			} //end case
 +			case COMP_CLUSTER:
 +			{
 +				if (!qfiles) Log_Print("no files found\n");
 +				for (qf = qfiles; qf; qf = qf->next)
 +				{
 +					AASOuputFile(qf, outputpath, filename);
 +					//
 +					Log_Print("cluster: %s to %s\n", qf->origname, filename);
 +					if (qf->type != QFILETYPE_BSP) Warning("%s is probably not a BSP file\n", qf->origname);
 +					//if the AAS file exists in the output directory
 +					if (!access(filename, 0x04))
 +					{
 +						if (!AAS_LoadAASFile(filename, 0, 0))
 +						{
 +							Error("error loading aas file %s\n", filename);
 +						} //end if
 +						//assume it's a Quake3 BSP file
 +						loadedmaptype = MAPTYPE_QUAKE3;
 +						//if it's a Quake3 map calculate the clusters
 +						if (loadedmaptype == MAPTYPE_QUAKE3)
 +						{
 +							aasworld.numclusters = 0;
 +							AAS_InitBotImport();
 +							AAS_InitClustering();
 +						} //end if
 +					} //end if
 +					else
 +					{
 +						Warning("AAS file %s not found in output folder\n", filename);
 +						Log_Print("creating %s...\n", filename);
 +						//set before map loading
 +						create_aas = 1;
 +						LoadMapFromBSP(qf);
 +						//create the AAS file
 +						AAS_Create(filename);
 +						//if it's a Quake3 map calculate the reachabilities and clusters
 +						if (loadedmaptype == MAPTYPE_QUAKE3) AAS_CalcReachAndClusters(qf);
 +					} //end else
 +					//
 +					if (optimize) AAS_Optimize();
 +					//write out the stored AAS file
 +					if (!AAS_WriteAASFile(filename))
 +					{
 +						Error("error writing %s\n", filename);
 +					} //end if
 +					//deallocate memory
 +					AAS_FreeMaxAAS();
 +				} //end for
 +				break;
 +			} //end case
 +			case COMP_AASOPTIMIZE:
 +			{
 +				if (!qfiles) Log_Print("no files found\n");
 +				for (qf = qfiles; qf; qf = qf->next)
 +				{
 +					AASOuputFile(qf, outputpath, filename);
 +					//
 +					Log_Print("optimizing: %s to %s\n", qf->origname, filename);
 +					if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname);
 +					//
 +					AAS_InitBotImport();
 +					//
 +					if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length))
 +					{
 +						Error("error loading aas file %s\n", qf->filename);
 +					} //end if
 +					AAS_Optimize();
 +					//write out the stored AAS file
 +					if (!AAS_WriteAASFile(filename))
 +					{
 +						Error("error writing %s\n", filename);
 +					} //end if
 +					//deallocate memory
 +					AAS_FreeMaxAAS();
 +				} //end for
 +				break;
 +			} //end case
 +			case COMP_AASINFO:
 +			{
 +				if (!qfiles) Log_Print("no files found\n");
 +				for (qf = qfiles; qf; qf = qf->next)
 +				{
 +					AASOuputFile(qf, outputpath, filename);
 +					//
 +					Log_Print("aas info for: %s\n", filename);
 +					if (qf->type != QFILETYPE_AAS) Warning("%s is probably not a AAS file\n", qf->origname);
 +					//
 +					AAS_InitBotImport();
 +					//
 +					if (!AAS_LoadAASFile(qf->filename, qf->offset, qf->length))
 +					{
 +						Error("error loading aas file %s\n", qf->filename);
 +					} //end if
 +					AAS_ShowTotals();
 +				} //end for
 +			} //end case
 +			default:
 +			{
 +				Log_Print("don't know what to do\n");
 +				break;
 +			} //end default
 +		} //end switch
 +	} //end if
 +	else
 +	{
 +		Log_Print("Usage:   bspc [-<switch> [-<switch> ...]]\n"
 +#if defined(WIN32) || defined(_WIN32)
 +			"Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n"
 +			"Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n"
 +#else
 +			"Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n"
 +			"Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n"
 +#endif
 +			"\n"
 +			"Switches:\n"
 +			//"   bsp2map  <[pakfilter/]filter.bsp>    = convert BSP to MAP\n"
 +			//"   aasall   <quake3folder>              = create AAS files for all BSPs\n"
 +			"   bsp2aas  <[pakfilter/]filter.bsp>    = convert BSP to AAS\n"
 +			"   reach    <filter.bsp>                = compute reachability & clusters\n"
 +			"   cluster  <filter.aas>                = compute clusters\n"
 +			"   aasopt   <filter.aas>                = optimize aas file\n"
 +			"   aasinfo  <filter.aas>                = show AAS file info\n"
 +			"   output   <output path>               = set output path\n"
 +			"   threads  <X>                         = set number of threads to X\n"
 +			"   cfg      <filename>                  = use this cfg file\n"
 +			"   optimize                             = enable optimization\n"
 +			"   noverbose                            = disable verbose output\n"
 +			"   breadthfirst                         = breadth first bsp building\n"
 +			"   nobrushmerge                         = don't merge brushes\n"
 +			"   noliquids                            = don't write liquids to map\n"
 +			"   freetree                             = free the bsp tree\n"
 +			"   nocsg                                = disables brush chopping\n"
 +			"   forcesidesvisible                    = force all sides to be visible\n"
 +			"   grapplereach                         = calculate grapple reachabilities\n"
 +
 +/*			"   glview     = output a GL view\n"
 +			"   draw       = enables drawing\n"
 +			"   noweld     = disables weld\n"
 +			"   noshare    = disables sharing\n"
 +			"   notjunc    = disables juncs\n"
 +			"   nowater    = disables water brushes\n"
 +			"   noprune    = disables node prunes\n"
 +			"   nomerge    = disables face merging\n"
 +			"   nosubdiv   = disables subdeviding\n"
 +			"   nodetail   = disables detail brushes\n"
 +			"   fulldetail = enables full detail\n"
 +			"   onlyents   = only compile entities with bsp\n"
 +			"   micro <volume>\n"
 +			"              = sets the micro volume to the given float\n"
 +			"   leaktest   = perform a leak test\n"
 +			"   verboseentities\n"
 +			"              = enable entity verbose mode\n"
 +			"   chop <subdivide_size>\n"
 +			"              = sets the subdivide size to the given float\n"*/
 +			"\n");
 +	} //end else
 +	Log_Print("BSPC run time is %5.0f seconds\n", I_FloatTime() - start_time);
 +	Log_Close();						//close the log file
 +	return 0;
 +} //end of the function main
 +
 diff --git a/code/bspc/bspc.sln b/code/bspc/bspc.sln new file mode 100755 index 0000000..79dc8ae --- /dev/null +++ b/code/bspc/bspc.sln @@ -0,0 +1,28 @@ +Microsoft Visual Studio Solution File, Format Version 8.00
 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bspc", "bspc.vcproj", "{4E4EBC16-F345-4667-84E1-86633BAFDAE6}"
 +	ProjectSection(ProjectDependencies) = postProject
 +	EndProjectSection
 +EndProject
 +Global
 +	GlobalSection(SourceCodeControl) = preSolution
 +		SccNumberOfProjects = 1
 +		SccProjectUniqueName0 = bspc.vcproj
 +		SccProjectName0 = \u0022$/source/code/bspc\u0022,\u0020IGAAAAAA
 +		SccLocalPath0 = .
 +		SccProvider0 = MSSCCI:Perforce\u0020SCM
 +	EndGlobalSection
 +	GlobalSection(SolutionConfiguration) = preSolution
 +		Debug = Debug
 +		Release = Release
 +	EndGlobalSection
 +	GlobalSection(ProjectConfiguration) = postSolution
 +		{4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Debug.ActiveCfg = Debug|Win32
 +		{4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Debug.Build.0 = Debug|Win32
 +		{4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Release.ActiveCfg = Release|Win32
 +		{4E4EBC16-F345-4667-84E1-86633BAFDAE6}.Release.Build.0 = Release|Win32
 +	EndGlobalSection
 +	GlobalSection(ExtensibilityGlobals) = postSolution
 +	EndGlobalSection
 +	GlobalSection(ExtensibilityAddIns) = postSolution
 +	EndGlobalSection
 +EndGlobal
 diff --git a/code/bspc/bspc.vcproj b/code/bspc/bspc.vcproj new file mode 100755 index 0000000..6171e1a --- /dev/null +++ b/code/bspc/bspc.vcproj @@ -0,0 +1,1381 @@ +<?xml version="1.0" encoding="Windows-1252"?>
 +<VisualStudioProject
 +	ProjectType="Visual C++"
 +	Version="7.10"
 +	Name="bspc"
 +	SccProjectName=""$/source/code/bspc", IGAAAAAA"
 +	SccLocalPath=".">
 +	<Platforms>
 +		<Platform
 +			Name="Win32"/>
 +	</Platforms>
 +	<Configurations>
 +		<Configuration
 +			Name="Debug|Win32"
 +			OutputDirectory=".\Debug"
 +			IntermediateDirectory=".\Debug"
 +			ConfigurationType="1"
 +			UseOfMFC="0"
 +			ATLMinimizesCRunTimeLibraryUsage="FALSE"
 +			CharacterSet="2">
 +			<Tool
 +				Name="VCCLCompilerTool"
 +				Optimization="0"
 +				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;BSPC"
 +				BasicRuntimeChecks="3"
 +				RuntimeLibrary="1"
 +				UsePrecompiledHeader="2"
 +				PrecompiledHeaderFile=".\Debug/bspc.pch"
 +				AssemblerListingLocation=".\Debug/"
 +				ObjectFile=".\Debug/"
 +				ProgramDataBaseFileName=".\Debug/"
 +				WarningLevel="3"
 +				SuppressStartupBanner="TRUE"
 +				DebugInformationFormat="4"/>
 +			<Tool
 +				Name="VCCustomBuildTool"/>
 +			<Tool
 +				Name="VCLinkerTool"
 +				OutputFile="..\Debug\bspc.exe"
 +				LinkIncremental="1"
 +				SuppressStartupBanner="TRUE"
 +				GenerateDebugInformation="TRUE"
 +				ProgramDatabaseFile=".\Debug/bspc.pdb"
 +				SubSystem="1"
 +				TargetMachine="1"/>
 +			<Tool
 +				Name="VCMIDLTool"
 +				TypeLibraryName=".\Debug/bspc.tlb"
 +				HeaderFileName=""/>
 +			<Tool
 +				Name="VCPostBuildEventTool"/>
 +			<Tool
 +				Name="VCPreBuildEventTool"/>
 +			<Tool
 +				Name="VCPreLinkEventTool"/>
 +			<Tool
 +				Name="VCResourceCompilerTool"
 +				PreprocessorDefinitions="_DEBUG"
 +				Culture="1033"/>
 +			<Tool
 +				Name="VCWebServiceProxyGeneratorTool"/>
 +			<Tool
 +				Name="VCXMLDataGeneratorTool"/>
 +			<Tool
 +				Name="VCWebDeploymentTool"/>
 +			<Tool
 +				Name="VCManagedWrapperGeneratorTool"/>
 +			<Tool
 +				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
 +		</Configuration>
 +		<Configuration
 +			Name="Release|Win32"
 +			OutputDirectory=".\Release"
 +			IntermediateDirectory=".\Release"
 +			ConfigurationType="1"
 +			UseOfMFC="0"
 +			ATLMinimizesCRunTimeLibraryUsage="FALSE"
 +			CharacterSet="2">
 +			<Tool
 +				Name="VCCLCompilerTool"
 +				Optimization="2"
 +				InlineFunctionExpansion="1"
 +				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;BSPC"
 +				StringPooling="TRUE"
 +				RuntimeLibrary="0"
 +				EnableFunctionLevelLinking="TRUE"
 +				UsePrecompiledHeader="2"
 +				PrecompiledHeaderFile=".\Release/bspc.pch"
 +				AssemblerListingLocation=".\Release/"
 +				ObjectFile=".\Release/"
 +				ProgramDataBaseFileName=".\Release/"
 +				WarningLevel="3"
 +				SuppressStartupBanner="TRUE"/>
 +			<Tool
 +				Name="VCCustomBuildTool"/>
 +			<Tool
 +				Name="VCLinkerTool"
 +				OutputFile="..\Release\bspc.exe"
 +				LinkIncremental="1"
 +				SuppressStartupBanner="TRUE"
 +				ProgramDatabaseFile=".\Release/bspc.pdb"
 +				SubSystem="1"
 +				TargetMachine="1"/>
 +			<Tool
 +				Name="VCMIDLTool"
 +				TypeLibraryName=".\Release/bspc.tlb"
 +				HeaderFileName=""/>
 +			<Tool
 +				Name="VCPostBuildEventTool"/>
 +			<Tool
 +				Name="VCPreBuildEventTool"/>
 +			<Tool
 +				Name="VCPreLinkEventTool"/>
 +			<Tool
 +				Name="VCResourceCompilerTool"
 +				PreprocessorDefinitions="NDEBUG"
 +				Culture="1033"/>
 +			<Tool
 +				Name="VCWebServiceProxyGeneratorTool"/>
 +			<Tool
 +				Name="VCXMLDataGeneratorTool"/>
 +			<Tool
 +				Name="VCWebDeploymentTool"/>
 +			<Tool
 +				Name="VCManagedWrapperGeneratorTool"/>
 +			<Tool
 +				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
 +		</Configuration>
 +	</Configurations>
 +	<References>
 +	</References>
 +	<Files>
 +		<Filter
 +			Name="Source Files"
 +			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
 +			<File
 +				RelativePath="_files.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_areamerging.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_cfg.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_create.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_edgemelting.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_facemerging.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_file.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_gsubdiv.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_map.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_prunenodes.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="aas_store.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="be_aas_bspc.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_bspq3.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_cluster.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_move.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_optimize.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_reach.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_sample.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="brushbsp.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="bspc.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_load.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_patch.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_test.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_trace.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="csg.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="glfile.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_bsp_ent.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_bsp_hl.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_bsp_q1.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_bsp_q2.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_bsp_q3.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_bsp_sin.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_cmd.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_libvar.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_log.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_math.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_mem.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_poly.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_precomp.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_qfiles.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_script.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_struct.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_threads.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="l_utils.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="leakfile.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="map.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="map_hl.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="map_q1.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="map_q2.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="map_q3.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="map_sin.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\md4.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="nodraw.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="portals.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="textures.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="tree.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\unzip.c">
 +				<FileConfiguration
 +					Name="Debug|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="0"
 +						PreprocessorDefinitions=""
 +						BasicRuntimeChecks="3"/>
 +				</FileConfiguration>
 +				<FileConfiguration
 +					Name="Release|Win32">
 +					<Tool
 +						Name="VCCLCompilerTool"
 +						Optimization="2"
 +						PreprocessorDefinitions=""/>
 +				</FileConfiguration>
 +			</File>
 +		</Filter>
 +		<Filter
 +			Name="Header Files"
 +			Filter="h;hpp;hxx;hm;inl">
 +			<File
 +				RelativePath="aas_areamerging.h">
 +			</File>
 +			<File
 +				RelativePath="aas_cfg.h">
 +			</File>
 +			<File
 +				RelativePath="aas_create.h">
 +			</File>
 +			<File
 +				RelativePath="aas_edgemelting.h">
 +			</File>
 +			<File
 +				RelativePath="aas_facemerging.h">
 +			</File>
 +			<File
 +				RelativePath="aas_file.h">
 +			</File>
 +			<File
 +				RelativePath="aas_gsubdiv.h">
 +			</File>
 +			<File
 +				RelativePath="aas_map.h">
 +			</File>
 +			<File
 +				RelativePath="aas_prunenodes.h">
 +			</File>
 +			<File
 +				RelativePath="aas_store.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\aasfile.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_bsp.h">
 +			</File>
 +			<File
 +				RelativePath="be_aas_bspc.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_cluster.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_debug.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_def.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_entity.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_file.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_funcs.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_main.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_move.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_optimize.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_reach.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_route.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_routealt.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_aas_sample.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\be_interface.h">
 +			</File>
 +			<File
 +				RelativePath="..\game\bg_public.h">
 +			</File>
 +			<File
 +				RelativePath="..\cgame\cg_public.h">
 +			</File>
 +			<File
 +				RelativePath="..\client\client.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_local.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_patch.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_polylib.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\cm_public.h">
 +			</File>
 +			<File
 +				RelativePath="..\ui\keycodes.h">
 +			</File>
 +			<File
 +				RelativePath="..\client\keys.h">
 +			</File>
 +			<File
 +				RelativePath="l_bsp_ent.h">
 +			</File>
 +			<File
 +				RelativePath="l_bsp_hl.h">
 +			</File>
 +			<File
 +				RelativePath="l_bsp_q1.h">
 +			</File>
 +			<File
 +				RelativePath="l_bsp_q2.h">
 +			</File>
 +			<File
 +				RelativePath="l_bsp_q3.h">
 +			</File>
 +			<File
 +				RelativePath="l_bsp_sin.h">
 +			</File>
 +			<File
 +				RelativePath="l_cmd.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_libvar.h">
 +			</File>
 +			<File
 +				RelativePath="l_log.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_log.h">
 +			</File>
 +			<File
 +				RelativePath="l_math.h">
 +			</File>
 +			<File
 +				RelativePath="l_mem.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_memory.h">
 +			</File>
 +			<File
 +				RelativePath="l_poly.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_precomp.h">
 +			</File>
 +			<File
 +				RelativePath="l_qfiles.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_script.h">
 +			</File>
 +			<File
 +				RelativePath="..\botlib\l_struct.h">
 +			</File>
 +			<File
 +				RelativePath="l_threads.h">
 +			</File>
 +			<File
 +				RelativePath="l_utils.h">
 +			</File>
 +			<File
 +				RelativePath="q2files.h">
 +			</File>
 +			<File
 +				RelativePath="q3files.h">
 +			</File>
 +			<File
 +				RelativePath="..\game\q_shared.h">
 +			</File>
 +			<File
 +				RelativePath="qbsp.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\qcommon.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\qfiles.h">
 +			</File>
 +			<File
 +				RelativePath="sinfiles.h">
 +			</File>
 +			<File
 +				RelativePath="..\client\snd_public.h">
 +			</File>
 +			<File
 +				RelativePath="..\game\surfaceflags.h">
 +			</File>
 +			<File
 +				RelativePath="..\renderer\tr_public.h">
 +			</File>
 +			<File
 +				RelativePath="..\cgame\tr_types.h">
 +			</File>
 +			<File
 +				RelativePath="..\ui\ui_public.h">
 +			</File>
 +			<File
 +				RelativePath="..\qcommon\unzip.h">
 +			</File>
 +		</Filter>
 +		<Filter
 +			Name="Resource Files"
 +			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
 +		</Filter>
 +	</Files>
 +	<Globals>
 +	</Globals>
 +</VisualStudioProject>
 diff --git a/code/bspc/cfgq3.c b/code/bspc/cfgq3.c new file mode 100755 index 0000000..588509c --- /dev/null +++ b/code/bspc/cfgq3.c @@ -0,0 +1,84 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +//===========================================================================
 +// BSPC configuration file
 +// Quake3
 +//===========================================================================
 +
 +#define PRESENCE_NONE				1
 +#define PRESENCE_NORMAL				2
 +#define PRESENCE_CROUCH				4
 +
 +bbox	//30x30x56
 +{
 +	presencetype	PRESENCE_NORMAL
 +	flags			0x0000
 +	mins			{-15, -15, -24}
 +	maxs			{15, 15, 32}
 +} //end bbox
 +
 +bbox	//30x30x40
 +{
 +	presencetype	PRESENCE_CROUCH
 +	flags			0x0001
 +	mins			{-15, -15, -24}
 +	maxs			{15, 15, 16}
 +} //end bbox
 +
 +settings
 +{
 +	phys_gravitydirection		{0, 0, -1}
 +	phys_friction				6
 +	phys_stopspeed				100
 +	phys_gravity				800
 +	phys_waterfriction			1
 +	phys_watergravity			400
 +	phys_maxvelocity			320
 +	phys_maxwalkvelocity		320
 +	phys_maxcrouchvelocity		100
 +	phys_maxswimvelocity		150
 +	phys_maxacceleration		2200
 +	phys_airaccelerate			0
 +	phys_maxstep				18
 +	phys_maxsteepness			0.7
 +	phys_maxwaterjump			19
 +	phys_maxbarrier				33
 +	phys_jumpvel				270
 +	phys_falldelta5				40
 +	phys_falldelta10			60
 +	rs_waterjump				400
 +	rs_teleport					50
 +	rs_barrierjump				100
 +	rs_startcrouch				300
 +	rs_startgrapple				500
 +	rs_startwalkoffledge		70
 +	rs_startjump				300
 +	rs_rocketjump				500
 +	rs_bfgjump					500
 +	rs_jumppad					250
 +	rs_aircontrolledjumppad		300
 +	rs_funcbob					300
 +	rs_startelevator			50
 +	rs_falldamage5				300
 +	rs_falldamage10				500
 +	rs_maxjumpfallheight		450
 +} //end settings
 diff --git a/code/bspc/csg.c b/code/bspc/csg.c new file mode 100755 index 0000000..c141075 --- /dev/null +++ b/code/bspc/csg.c @@ -0,0 +1,1005 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +
 +/*
 +
 +tag all brushes with original contents
 +brushes may contain multiple contents
 +there will be no brush overlap after csg phase
 +
 +*/
 +
 +int minplanenums[3];
 +int maxplanenums[3];
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void CheckBSPBrush(bspbrush_t *brush)
 +{
 +	int i, j;
 +	plane_t *plane1, *plane2;
 +
 +	//check if the brush is convex... flipped planes make a brush non-convex
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		for (j = 0; j < brush->numsides; j++)
 +		{
 +			if (i == j) continue;
 +			plane1 = &mapplanes[brush->sides[i].planenum];
 +			plane2 = &mapplanes[brush->sides[j].planenum];
 +			//
 +			if (WindingsNonConvex(brush->sides[i].winding,
 +									brush->sides[j].winding,
 +									plane1->normal, plane2->normal,
 +									plane1->dist, plane2->dist))
 +			{
 +				Log_Print("non convex brush");
 +				break;
 +			} //end if
 +		} //end for
 +	} //end for
 +	BoundBrush(brush);
 +	//check for out of bound brushes
 +	for (i = 0; i < 3; i++)
 +	{
 +		if (brush->mins[i] < -MAX_MAP_BOUNDS || brush->maxs[i] > MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("brush: bounds out of range\n");
 +			Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i]);
 +			break;
 +		} //end if
 +		if (brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("brush: no visible sides on brush\n");
 +			Log_Print("ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i]);
 +			break;
 +		} //end if
 +	} //end for
 +} //end of the function CheckBSPBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void BSPBrushWindings(bspbrush_t *brush)
 +{
 +	int i, j;
 +	winding_t *w;
 +	plane_t *plane;
 +
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		plane = &mapplanes[brush->sides[i].planenum];
 +		w = BaseWindingForPlane(plane->normal, plane->dist);
 +		for (j = 0; j < brush->numsides && w; j++)
 +		{
 +			if (i == j) continue;
 +			plane = &mapplanes[brush->sides[j].planenum^1];
 +			ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
 +		} //end for
 +		brush->sides[i].winding = w;
 +	} //end for
 +} //end of the function BSPBrushWindings
 +//===========================================================================
 +// NOTE: can't keep brush->original intact
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *TryMergeBrushes(bspbrush_t *brush1, bspbrush_t *brush2)
 +{
 +	int i, j, k, n, shared;
 +	side_t *side1, *side2, *cs;
 +	plane_t *plane1, *plane2;
 +	bspbrush_t *newbrush;
 +
 +	//check for bounding box overlapp
 +	for (i = 0; i < 3; i++)
 +	{
 +		if (brush1->mins[i] > brush2->maxs[i] + 2
 +				|| brush1->maxs[i] < brush2->mins[i] - 2)
 +		{
 +			return NULL;
 +		} //end if
 +	} //end for
 +	//
 +	shared = 0;
 +	//check if the brush is convex... flipped planes make a brush non-convex
 +	for (i = 0; i < brush1->numsides; i++)
 +	{
 +		side1 = &brush1->sides[i];
 +		//don't check the "shared" sides
 +		for (k = 0; k < brush2->numsides; k++)
 +		{
 +			side2 = &brush2->sides[k];
 +			if (side1->planenum == (side2->planenum^1))
 +			{
 +				shared++;
 +				//there may only be ONE shared side
 +				if (shared > 1) return NULL;
 +				break;
 +			} //end if
 +		} //end for
 +		if (k < brush2->numsides) continue;
 +		//
 +		for (j = 0; j < brush2->numsides; j++)
 +		{
 +			side2 = &brush2->sides[j];
 +			//don't check the "shared" sides
 +			for (n = 0; n < brush1->numsides; n++)
 +			{
 +				side1 = &brush1->sides[n];
 +				if (side1->planenum == (side2->planenum^1)) break;
 +			} //end for
 +			if (n < brush1->numsides) continue;
 +			//
 +			side1 = &brush1->sides[i];
 +			//if the side is in the same plane
 +			//*
 +			if (side1->planenum == side2->planenum)
 +			{
 +				if (side1->texinfo != TEXINFO_NODE &&
 +					side2->texinfo != TEXINFO_NODE &&
 +					side1->texinfo != side2->texinfo) return NULL;
 +				continue;
 +			} //end if
 +			//
 +			plane1 = &mapplanes[side1->planenum];
 +			plane2 = &mapplanes[side2->planenum];
 +			//
 +			if (WindingsNonConvex(side1->winding, side2->winding,
 +									plane1->normal, plane2->normal,
 +									plane1->dist, plane2->dist))
 +			{
 +				return NULL;
 +			} //end if
 +		} //end for
 +	} //end for
 +	newbrush = AllocBrush(brush1->numsides + brush2->numsides);
 +	newbrush->original = brush1->original;
 +	newbrush->numsides = 0;
 +	//newbrush->side = brush1->side;	//brush contents
 +	//fix texinfos for sides lying in the same plane
 +	for (i = 0; i < brush1->numsides; i++)
 +	{
 +		side1 = &brush1->sides[i];
 +		//
 +		for (n = 0; n < brush2->numsides; n++)
 +		{
 +			side2 = &brush2->sides[n];
 +			//if both sides are in the same plane get the texinfo right
 +			if (side1->planenum == side2->planenum)
 +			{
 +				if (side1->texinfo == TEXINFO_NODE) side1->texinfo = side2->texinfo;
 +				if (side2->texinfo == TEXINFO_NODE) side2->texinfo = side1->texinfo;
 +			} //end if
 +		} //end for
 +	} //end for
 +	//
 +	for (i = 0; i < brush1->numsides; i++)
 +	{
 +		side1 = &brush1->sides[i];
 +		//don't add the "shared" sides
 +		for (n = 0; n < brush2->numsides; n++)
 +		{
 +			side2 = &brush2->sides[n];
 +			if (side1->planenum == (side2->planenum ^ 1)) break;
 +		} //end for
 +		if (n < brush2->numsides) continue;
 +		//
 +		for (n = 0; n < newbrush->numsides; n++)
 +		{
 +			cs = &newbrush->sides[n];
 +			if (cs->planenum == side1->planenum)
 +			{
 +				Log_Print("brush duplicate plane\n");
 +				break;
 +			} //end if
 +		} //end if
 +		if (n < newbrush->numsides) continue;
 +		//add this side
 +		cs = &newbrush->sides[newbrush->numsides];
 +		newbrush->numsides++;
 +		*cs = *side1;
 +	} //end for
 +	for (j = 0; j < brush2->numsides; j++)
 +	{
 +		side2 = &brush2->sides[j];
 +		for (n = 0; n < brush1->numsides; n++)
 +		{
 +			side1 = &brush1->sides[n];
 +			//if the side is in the same plane
 +			if (side2->planenum == side1->planenum) break;
 +			//don't add the "shared" sides
 +			if (side2->planenum == (side1->planenum ^ 1)) break;
 +		} //end for
 +		if (n < brush1->numsides) continue;
 +		//
 +		for (n = 0; n < newbrush->numsides; n++)
 +		{
 +			cs = &newbrush->sides[n];
 +			if (cs->planenum == side2->planenum)
 +			{
 +				Log_Print("brush duplicate plane\n");
 +				break;
 +			} //end if
 +		} //end if
 +		if (n < newbrush->numsides) continue;
 +		//add this side
 +		cs = &newbrush->sides[newbrush->numsides];
 +		newbrush->numsides++;
 +		*cs = *side2;
 +	} //end for
 +	BSPBrushWindings(newbrush);
 +	BoundBrush(newbrush);
 +	CheckBSPBrush(newbrush);
 +	return newbrush;
 +} //end of the function TryMergeBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *MergeBrushes(bspbrush_t *brushlist)
 +{
 +	int nummerges, merged;
 +	bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist;
 +	bspbrush_t *lastb2;
 +
 +	if (!brushlist) return NULL;
 +
 +	qprintf("%5d brushes merged", nummerges = 0);
 +	do
 +	{
 +		for (tail = brushlist; tail; tail = tail->next)
 +		{
 +			if (!tail->next) break;
 +		} //end for
 +		merged = 0;
 +		newbrushlist = NULL;
 +		for (b1 = brushlist; b1; b1 = brushlist)
 +		{
 +			lastb2 = b1;
 +			for (b2 = b1->next; b2; b2 = b2->next)
 +			{
 +				//if the brushes don't have the same contents
 +				if (b1->original->contents != b2->original->contents ||
 +					b1->original->expansionbbox != b2->original->expansionbbox) newbrush = NULL;
 +				else newbrush = TryMergeBrushes(b1, b2);
 +				if (newbrush)
 +				{
 +					tail->next = newbrush;
 +					lastb2->next = b2->next;
 +					brushlist = brushlist->next;
 +					FreeBrush(b1);
 +					FreeBrush(b2);
 +					for (tail = brushlist; tail; tail = tail->next)
 +					{
 +						if (!tail->next) break;
 +					} //end for
 +					merged++;
 +					qprintf("\r%5d", nummerges++);
 +					break;
 +				} //end if
 +				lastb2 = b2;
 +			} //end for
 +			//if b1 can't be merged with any of the other brushes
 +			if (!b2)
 +			{
 +				brushlist = brushlist->next;
 +				//keep b1
 +				b1->next = newbrushlist;
 +				newbrushlist = b1;
 +			} //end else
 +		} //end for
 +		brushlist = newbrushlist;
 +	} while(merged);
 +	qprintf("\n");
 +	return newbrushlist;
 +} //end of the function MergeBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void SplitBrush2 (bspbrush_t *brush, int planenum,
 +	bspbrush_t **front, bspbrush_t **back)
 +{
 +	SplitBrush (brush, planenum, front, back);
 +#if 0
 +	if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
 +		(*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo;	// not -1
 +	if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
 +		(*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo;	// not -1
 +#endif
 +} //end of the function SplitBrush2
 +//===========================================================================
 +// Returns a list of brushes that remain after B is subtracted from A.
 +// May by empty if A is contained inside B.
 +// The originals are undisturbed.
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
 +{	// a - b = out (list)
 +	int		i;
 +	bspbrush_t	*front, *back;
 +	bspbrush_t	*out, *in;
 +
 +	in = a;
 +	out = NULL;
 +	for (i = 0; i < b->numsides && in; i++)
 +	{
 +		SplitBrush2(in, b->sides[i].planenum, &front, &back);
 +		if (in != a) FreeBrush(in);
 +		if (front)
 +		{	// add to list
 +			front->next = out;
 +			out = front;
 +		} //end if
 +		in = back;
 +	} //end for
 +	if (in)
 +	{
 +		FreeBrush (in);
 +	} //end if
 +	else
 +	{	// didn't really intersect
 +		FreeBrushList (out);
 +		return a;
 +	} //end else
 +	return out;
 +} //end of the function SubtractBrush
 +//===========================================================================
 +// Returns a single brush made up by the intersection of the
 +// two provided brushes, or NULL if they are disjoint.
 +//
 +// The originals are undisturbed.
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
 +{
 +	int		i;
 +	bspbrush_t	*front, *back;
 +	bspbrush_t	*in;
 +
 +	in = a;
 +	for (i=0 ; i<b->numsides && in ; i++)
 +	{
 +		SplitBrush2(in, b->sides[i].planenum, &front, &back);
 +		if (in != a) FreeBrush(in);
 +		if (front) FreeBrush(front);
 +		in = back;
 +	} //end for
 +
 +	if (in == a) return NULL;
 +
 +	in->next = NULL;
 +	return in;
 +} //end of the function IntersectBrush
 +//===========================================================================
 +// Returns true if the two brushes definately do not intersect.
 +// There will be false negatives for some non-axial combinations.
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
 +{
 +	int		i, j;
 +
 +	// check bounding boxes
 +	for (i=0 ; i<3 ; i++)
 +		if (a->mins[i] >= b->maxs[i]
 +		|| a->maxs[i] <= b->mins[i])
 +			return true;	// bounding boxes don't overlap
 +
 +	// check for opposing planes
 +	for (i=0 ; i<a->numsides ; i++)
 +	{
 +		for (j=0 ; j<b->numsides ; j++)
 +		{
 +			if (a->sides[i].planenum ==
 +			(b->sides[j].planenum^1) )
 +				return true;	// opposite planes, so not touching
 +		}
 +	}
 +
 +	return false;	// might intersect
 +} //end of the function BrushesDisjoint
 +//===========================================================================
 +// Returns a content word for the intersection of two brushes.
 +// Some combinations will generate a combination (water + clip),
 +// but most will be the stronger of the two contents.
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int IntersectionContents (int c1, int c2)
 +{
 +	int out;
 +
 +	out = c1 | c2;
 +
 +	if (out & CONTENTS_SOLID) out = CONTENTS_SOLID;
 +
 +	return out;
 +} //end of the function IntersectionContents
 +//===========================================================================
 +// Any planes shared with the box edge will be set to no texinfo
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *ClipBrushToBox(bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs)
 +{
 +	int		i, j;
 +	bspbrush_t	*front,	*back;
 +	int		p;
 +
 +	for (j=0 ; j<2 ; j++)
 +	{
 +		if (brush->maxs[j] > clipmaxs[j])
 +		{
 +			SplitBrush (brush, maxplanenums[j], &front, &back);
 +			if (front)
 +				FreeBrush (front);
 +			brush = back;
 +			if (!brush)
 +				return NULL;
 +		}
 +		if (brush->mins[j] < clipmins[j])
 +		{
 +			SplitBrush (brush, minplanenums[j], &front, &back);
 +			if (back)
 +				FreeBrush (back);
 +			brush = front;
 +			if (!brush)
 +				return NULL;
 +		}
 +	}
 +
 +	// remove any colinear faces
 +
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		p = brush->sides[i].planenum & ~1;
 +		if (p == maxplanenums[0] || p == maxplanenums[1] 
 +			|| p == minplanenums[0] || p == minplanenums[1])
 +		{
 +			brush->sides[i].texinfo = TEXINFO_NODE;
 +			brush->sides[i].flags &= ~SFL_VISIBLE;
 +		}
 +	}
 +	return brush;
 +} //end of the function ClipBrushToBox
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *MakeBspBrushList(int startbrush, int endbrush,
 +											vec3_t clipmins, vec3_t clipmaxs)
 +{
 +	mapbrush_t	*mb;
 +	bspbrush_t	*brushlist, *newbrush;
 +	int			i, j;
 +	int			c_faces;
 +	int			c_brushes;
 +	int			numsides;
 +	int			vis;
 +	vec3_t		normal;
 +	float		dist;
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		VectorClear (normal);
 +		normal[i] = 1;
 +		dist = clipmaxs[i];
 +		maxplanenums[i] = FindFloatPlane(normal, dist);
 +		dist = clipmins[i];
 +		minplanenums[i] = FindFloatPlane(normal, dist);
 +	}
 +
 +	brushlist = NULL;
 +	c_faces = 0;
 +	c_brushes = 0;
 +
 +	for (i=startbrush ; i<endbrush ; i++)
 +	{
 +		mb = &mapbrushes[i];
 +
 +		numsides = mb->numsides;
 +		if (!numsides)
 +			continue;
 +
 +		// make sure the brush has at least one face showing
 +		vis = 0;
 +		for (j=0 ; j<numsides ; j++)
 +			if ((mb->original_sides[j].flags & SFL_VISIBLE) && mb->original_sides[j].winding)
 +				vis++;
 +#if 0
 +		if (!vis)
 +			continue;	// no faces at all
 +#endif
 +		// if the brush is outside the clip area, skip it
 +		for (j=0 ; j<3 ; j++)
 +			if (mb->mins[j] >= clipmaxs[j]
 +			|| mb->maxs[j] <= clipmins[j])
 +			break;
 +		if (j != 3)
 +			continue;
 +
 +		//
 +		// make a copy of the brush
 +		//
 +		newbrush = AllocBrush (mb->numsides);
 +		newbrush->original = mb;
 +		newbrush->numsides = mb->numsides;
 +		memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t));
 +		for (j=0 ; j<numsides ; j++)
 +		{
 +			if (newbrush->sides[j].winding)
 +				newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
 +			if (newbrush->sides[j].surf & SURF_HINT)
 +				newbrush->sides[j].flags |= SFL_VISIBLE;	// hints are always visible
 +		}
 +		VectorCopy (mb->mins, newbrush->mins);
 +		VectorCopy (mb->maxs, newbrush->maxs);
 +
 +		//
 +		// carve off anything outside the clip box
 +		//
 +		newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
 +		if (!newbrush)
 +			continue;
 +
 +		c_faces += vis;
 +		c_brushes++;
 +
 +		newbrush->next = brushlist;
 +		brushlist = newbrush;
 +	}
 +
 +	return brushlist;
 +} //end of the function MakeBspBrushList
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
 +{
 +	bspbrush_t	*walk, *next;
 +
 +	for (walk=list ; walk ; walk=next)
 +	{	// add to end of list
 +		next = walk->next;
 +		walk->next = NULL;
 +		tail->next = walk;
 +		tail = walk;
 +	} //end for
 +	return tail;
 +} //end of the function AddBrushListToTail
 +//===========================================================================
 +// Builds a new list that doesn't hold the given brush
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *CullList(bspbrush_t *list, bspbrush_t *skip1)
 +{
 +	bspbrush_t	*newlist;
 +	bspbrush_t	*next;
 +
 +	newlist = NULL;
 +
 +	for ( ; list ; list = next)
 +	{
 +		next = list->next;
 +		if (list == skip1)
 +		{
 +			FreeBrush (list);
 +			continue;
 +		}
 +		list->next = newlist;
 +		newlist = list;
 +	}
 +	return newlist;
 +} //end of the function CullList
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +/*
 +void WriteBrushMap(char *name, bspbrush_t *list)
 +{
 +	FILE	*f;
 +	side_t	*s;
 +	int		i;
 +	winding_t	*w;
 +
 +	Log_Print("writing %s\n", name);
 +	f = fopen (name, "wb");
 +	if (!f)
 +		Error ("Can't write %s\b", name);
 +
 +	fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
 +
 +	for ( ; list ; list=list->next )
 +	{
 +		fprintf (f, "{\n");
 +		for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
 +		{
 +			w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
 +
 +			fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
 +			fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
 +			fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
 +
 +			fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
 +			FreeWinding (w);
 +		}
 +		fprintf (f, "}\n");
 +	}
 +	fprintf (f, "}\n");
 +
 +	fclose (f);
 +} //end of the function WriteBrushMap
 +*/
 +//===========================================================================
 +// Returns true if b1 is allowed to bite b2
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
 +{
 +#ifdef ME
 +	if (create_aas)
 +	{
 +		if (b1->original->expansionbbox != b2->original->expansionbbox)
 +		{
 +			return false;
 +		} //end if
 +		//never have something else bite a ladder brush
 +		//never have a ladder brush bite something else
 +		if ( (b1->original->contents & CONTENTS_LADDER)
 +			&& !(b2->original->contents & CONTENTS_LADDER))
 +		{ 
 +			return false;
 +		} //end if
 +	} //end if
 +#endif //ME
 +	// detail brushes never bite structural brushes
 +	if ( (b1->original->contents & CONTENTS_DETAIL) 
 +		&& !(b2->original->contents & CONTENTS_DETAIL) )
 +	{
 +		return false;
 +	} //end if
 +	if (b1->original->contents & CONTENTS_SOLID)
 +	{
 +		return true;
 +	} //end if
 +	return false;
 +} //end of the function BrushGE
 +//===========================================================================
 +// Carves any intersecting solid brushes into the minimum number
 +// of non-intersecting brushes.
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *ChopBrushes (bspbrush_t *head)
 +{
 +	bspbrush_t	*b1, *b2, *next;
 +	bspbrush_t	*tail;
 +	bspbrush_t	*keep;
 +	bspbrush_t	*sub, *sub2;
 +	int			c1, c2;
 +	int num_csg_iterations;
 +
 +	Log_Print("-------- Brush CSG ---------\n");
 +	Log_Print("%6d original brushes\n", CountBrushList (head));
 +
 +	num_csg_iterations = 0;
 +	qprintf("%6d output brushes", num_csg_iterations);
 +
 +#if 0
 +	if (startbrush == 0)
 +		WriteBrushList ("before.gl", head, false);
 +#endif
 +	keep = NULL;
 +
 +newlist:
 +	// find tail
 +	if (!head) return NULL;
 +
 +	for (tail = head; tail->next; tail = tail->next)
 +		;
 +
 +	for (b1=head ; b1 ; b1=next)
 +	{
 +		next = b1->next;
 +
 +		//if the conversion is cancelled
 +		if (cancelconversion)
 +		{
 +			b1->next = keep;
 +			keep = b1;
 +			continue;
 +		} //end if
 +		
 +		for (b2 = b1->next; b2; b2 = b2->next)
 +		{
 +			if (BrushesDisjoint (b1, b2))
 +				continue;
 +
 +			sub = NULL;
 +			sub2 = NULL;
 +			c1 = 999999;
 +			c2 = 999999;
 +
 +			if (BrushGE (b2, b1))
 +			{
 +				sub = SubtractBrush (b1, b2);
 +				if (sub == b1)
 +				{
 +					continue;		// didn't really intersect
 +				} //end if
 +				if (!sub)
 +				{	// b1 is swallowed by b2
 +					head = CullList (b1, b1);
 +					goto newlist;
 +				}
 +				c1 = CountBrushList (sub);
 +			}
 +
 +			if ( BrushGE (b1, b2) )
 +			{
 +				sub2 = SubtractBrush (b2, b1);
 +				if (sub2 == b2)
 +					continue;		// didn't really intersect
 +				if (!sub2)
 +				{	// b2 is swallowed by b1
 +					FreeBrushList (sub);
 +					head = CullList (b1, b2);
 +					goto newlist;
 +				}
 +				c2 = CountBrushList (sub2);
 +			}
 +
 +			if (!sub && !sub2)
 +				continue;		// neither one can bite
 +
 +			// only accept if it didn't fragment
 +			// (commenting this out allows full fragmentation)
 +			if (c1 > 1 && c2 > 1)
 +			{
 +				if (sub2)
 +					FreeBrushList (sub2);
 +				if (sub)
 +					FreeBrushList (sub);
 +				continue;
 +			}
 +
 +			if (c1 < c2)
 +			{
 +				if (sub2) FreeBrushList (sub2);
 +				tail = AddBrushListToTail (sub, tail);
 +				head = CullList (b1, b1);
 +				goto newlist;
 +			} //end if
 +			else
 +			{
 +				if (sub) FreeBrushList (sub);
 +				tail = AddBrushListToTail (sub2, tail);
 +				head = CullList (b1, b2);
 +				goto newlist;
 +			} //end else
 +		} //end for
 +
 +		if (!b2)
 +		{	// b1 is no longer intersecting anything, so keep it
 +			b1->next = keep;
 +			keep = b1;
 +		} //end if
 +		num_csg_iterations++;
 +		qprintf("\r%6d", num_csg_iterations);
 +	} //end for
 +
 +	if (cancelconversion) return keep;
 +	//
 +	qprintf("\n");
 +	Log_Write("%6d output brushes\r\n", num_csg_iterations);
 +
 +#if 0
 +	{
 +		WriteBrushList ("after.gl", keep, false);
 +		WriteBrushMap ("after.map", keep);
 +	}
 +#endif
 +
 +	return keep;
 +} //end of the function ChopBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *InitialBrushList (bspbrush_t *list)
 +{
 +	bspbrush_t *b;
 +	bspbrush_t	*out, *newb;
 +	int			i;
 +
 +	// only return brushes that have visible faces
 +	out = NULL;
 +	for (b=list ; b ; b=b->next)
 +	{
 +#if 0
 +		for (i=0 ; i<b->numsides ; i++)
 +			if (b->sides[i].flags & SFL_VISIBLE)
 +				break;
 +		if (i == b->numsides)
 +			continue;
 +#endif
 +		newb = CopyBrush (b);
 +		newb->next = out;
 +		out = newb;
 +
 +		// clear visible, so it must be set by MarkVisibleFaces_r
 +		// to be used in the optimized list
 +		for (i=0 ; i<b->numsides ; i++)
 +		{
 +			newb->sides[i].original = &b->sides[i];
 +//			newb->sides[i].visible = true;
 +			b->sides[i].flags &= ~SFL_VISIBLE;
 +		}
 +	}
 +
 +	return out;
 +} //end of the function InitialBrushList
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *OptimizedBrushList (bspbrush_t *list)
 +{
 +	bspbrush_t *b;
 +	bspbrush_t	*out, *newb;
 +	int			i;
 +
 +	// only return brushes that have visible faces
 +	out = NULL;
 +	for (b=list ; b ; b=b->next)
 +	{
 +		for (i=0 ; i<b->numsides ; i++)
 +			if (b->sides[i].flags & SFL_VISIBLE)
 +				break;
 +		if (i == b->numsides)
 +			continue;
 +		newb = CopyBrush (b);
 +		newb->next = out;
 +		out = newb;
 +	} //end for
 +
 +//	WriteBrushList ("vis.gl", out, true);
 +	return out;
 +} //end of the function OptimizeBrushList
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +tree_t *ProcessWorldBrushes(int brush_start, int brush_end)
 +{
 +	bspbrush_t *brushes;
 +	tree_t *tree;
 +	node_t *node;
 +	vec3_t mins, maxs;
 +
 +	//take the whole world
 +	mins[0] = map_mins[0] - 8;
 +	mins[1] = map_mins[1] - 8;
 +	mins[2] = map_mins[2] - 8;
 +
 +	maxs[0] = map_maxs[0] + 8;
 +	maxs[1] = map_maxs[1] + 8;
 +	maxs[2] = map_maxs[2] + 8;
 +
 +	//reset the brush bsp
 +	ResetBrushBSP();
 +
 +	// the makelist and chopbrushes could be cached between the passes...
 +
 +	//create a list with brushes that are within the given mins/maxs
 +	//some brushes will be cut and only the part that falls within the
 +	//mins/maxs will be in the bush list
 +	brushes = MakeBspBrushList(brush_start, brush_end, mins, maxs);
 +	//
 +
 +	if (!brushes)
 +	{
 +		node = AllocNode ();
 +		node->planenum = PLANENUM_LEAF;
 +		node->contents = CONTENTS_SOLID;
 +
 +		tree = Tree_Alloc();
 +		tree->headnode = node;
 +		VectorCopy(mins, tree->mins);
 +		VectorCopy(maxs, tree->maxs);
 +	} //end if
 +	else
 +	{
 +		//Carves any intersecting solid brushes into the minimum number
 +		//of non-intersecting brushes. 
 +		if (!nocsg)
 +		{
 +			brushes = ChopBrushes(brushes);
 +			/*
 +			if (create_aas)
 +			{
 +				brushes = MergeBrushes(brushes);
 +			} //end if*/
 +		} //end if
 +		//if the conversion is cancelled
 +		if (cancelconversion)
 +		{
 +			FreeBrushList(brushes);
 +			return NULL;
 +		} //end if
 +		//create the actual bsp tree
 +		tree = BrushBSP(brushes, mins, maxs);
 +	} //end else
 +	//return the tree
 +	return tree;
 +} //end of the function ProcessWorldBrushes
 diff --git a/code/bspc/faces.c b/code/bspc/faces.c new file mode 100755 index 0000000..bcd3ef9 --- /dev/null +++ b/code/bspc/faces.c @@ -0,0 +1,978 @@ +/*
 +===========================================================================
 +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);
 +}
 diff --git a/code/bspc/gldraw.c b/code/bspc/gldraw.c new file mode 100755 index 0000000..9f7d6f7 --- /dev/null +++ b/code/bspc/gldraw.c @@ -0,0 +1,232 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include <windows.h>
 +#include <GL/gl.h>
 +#include <GL/glu.h>
 +#include <GL/glaux.h>
 +
 +#include "qbsp.h"
 +
 +// can't use the glvertex3fv functions, because the vec3_t fields
 +// could be either floats or doubles, depending on DOUBLEVEC_T
 +
 +qboolean	drawflag;
 +vec3_t	draw_mins, draw_maxs;
 +
 +
 +#define	WIN_SIZE	512
 +
 +void InitWindow (void)
 +{
 +    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
 +    auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
 +    auxInitWindow ("qcsg");
 +}
 +
 +void Draw_ClearWindow (void)
 +{
 +	static int	init;
 +	int		w, h, g;
 +	vec_t	mx, my;
 +
 +	if (!drawflag)
 +		return;
 +
 +	if (!init)
 +	{
 +		init = true;
 +		InitWindow ();
 +	}
 +
 +	glClearColor (1,0.8,0.8,0);
 +	glClear (GL_COLOR_BUFFER_BIT);
 +
 +	w = (draw_maxs[0] - draw_mins[0]);
 +	h = (draw_maxs[1] - draw_mins[1]);
 +
 +	mx = draw_mins[0] + w/2;
 +	my = draw_mins[1] + h/2;
 +
 +	g = w > h ? w : h;
 +
 +	glLoadIdentity ();
 +    gluPerspective (90,  1,  2,  16384);
 +	gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
 +
 +	glColor3f (0,0,0);
 +//	glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
 +	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
 +	glDisable (GL_DEPTH_TEST);
 +	glEnable (GL_BLEND);
 +	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 +
 +#if 0
 +	glColor4f (1,0,0,0.5);
 +	glBegin (GL_POLYGON);
 +
 +	glVertex3f (0, 500, 0);
 +	glVertex3f (0, 900, 0);
 +	glVertex3f (0, 900, 100);
 +	glVertex3f (0, 500, 100);
 +
 +	glEnd ();
 +#endif
 +
 +	glFlush ();
 +
 +}
 +
 +void Draw_SetRed (void)
 +{
 +	if (!drawflag)
 +		return;
 +
 +	glColor3f (1,0,0);
 +}
 +
 +void Draw_SetGrey (void)
 +{
 +	if (!drawflag)
 +		return;
 +
 +	glColor3f (0.5,0.5,0.5);
 +}
 +
 +void Draw_SetBlack (void)
 +{
 +	if (!drawflag)
 +		return;
 +
 +	glColor3f (0,0,0);
 +}
 +
 +void DrawWinding (winding_t *w)
 +{
 +	int		i;
 +
 +	if (!drawflag)
 +		return;
 +
 +	glColor4f (0,0,0,0.5);
 +	glBegin (GL_LINE_LOOP);
 +	for (i=0 ; i<w->numpoints ; i++)
 +		glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
 +	glEnd ();
 +
 +	glColor4f (0,1,0,0.3);
 +	glBegin (GL_POLYGON);
 +	for (i=0 ; i<w->numpoints ; i++)
 +		glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
 +	glEnd ();
 +
 +	glFlush ();
 +}
 +
 +void DrawAuxWinding (winding_t *w)
 +{
 +	int		i;
 +
 +	if (!drawflag)
 +		return;
 +
 +	glColor4f (0,0,0,0.5);
 +	glBegin (GL_LINE_LOOP);
 +	for (i=0 ; i<w->numpoints ; i++)
 +		glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
 +	glEnd ();
 +
 +	glColor4f (1,0,0,0.3);
 +	glBegin (GL_POLYGON);
 +	for (i=0 ; i<w->numpoints ; i++)
 +		glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
 +	glEnd ();
 +
 +	glFlush ();
 +}
 +
 +//============================================================
 +
 +#define	GLSERV_PORT	25001
 +
 +qboolean	wins_init;
 +int			draw_socket;
 +
 +void GLS_BeginScene (void)
 +{
 +	WSADATA	winsockdata;
 +	WORD	wVersionRequested; 
 +	struct sockaddr_in	address;
 +	int		r;
 +
 +	if (!wins_init)
 +	{
 +		wins_init = true;
 +
 +		wVersionRequested = MAKEWORD(1, 1); 
 +
 +		r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
 +
 +		if (r)
 +			Error ("Winsock initialization failed.");
 +
 +	}
 +
 +	// connect a socket to the server
 +
 +	draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
 +	if (draw_socket == -1)
 +		Error ("draw_socket failed");
 +
 +	address.sin_family = AF_INET;
 +	address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 +	address.sin_port = GLSERV_PORT;
 +	r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
 +	if (r == -1)
 +	{
 +		closesocket (draw_socket);
 +		draw_socket = 0;
 +	}
 +}
 +
 +void GLS_Winding (winding_t *w, int code)
 +{
 +	byte	buf[1024];
 +	int		i, j;
 +
 +	if (!draw_socket)
 +		return;
 +
 +	((int *)buf)[0] = w->numpoints;
 +	((int *)buf)[1] = code;
 +	for (i=0 ; i<w->numpoints ; i++)
 +		for (j=0 ; j<3 ; j++)
 +			((float *)buf)[2+i*3+j] = w->p[i][j];
 +
 +	send (draw_socket, buf, w->numpoints*12+8, 0);
 +}
 +
 +void GLS_EndScene (void)
 +{
 +	closesocket (draw_socket);
 +	draw_socket = 0;
 +}
 diff --git a/code/bspc/glfile.c b/code/bspc/glfile.c new file mode 100755 index 0000000..179a836 --- /dev/null +++ b/code/bspc/glfile.c @@ -0,0 +1,149 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +
 +int		c_glfaces;
 +
 +int PortalVisibleSides (portal_t *p)
 +{
 +	int		fcon, bcon;
 +
 +	if (!p->onnode)
 +		return 0;		// outside
 +
 +	fcon = p->nodes[0]->contents;
 +	bcon = p->nodes[1]->contents;
 +
 +	// same contents never create a face
 +	if (fcon == bcon)
 +		return 0;
 +
 +	// FIXME: is this correct now?
 +	if (!fcon)
 +		return 1;
 +	if (!bcon)
 +		return 2;
 +	return 0;
 +}
 +
 +void OutputWinding (winding_t *w, FILE *glview)
 +{
 +	static	int	level = 128;
 +	vec_t		light;
 +	int			i;
 +
 +	fprintf (glview, "%i\n", w->numpoints);
 +	level+=28;
 +	light = (level&255)/255.0;
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
 +			w->p[i][0],
 +			w->p[i][1],
 +			w->p[i][2],
 +			light,
 +			light,
 +			light);
 +	}
 +	fprintf (glview, "\n");
 +}
 +
 +/*
 +=============
 +OutputPortal
 +=============
 +*/
 +void OutputPortal (portal_t *p, FILE *glview)
 +{
 +	winding_t	*w;
 +	int		sides;
 +
 +	sides = PortalVisibleSides (p);
 +	if (!sides)
 +		return;
 +
 +	c_glfaces++;
 +
 +	w = p->winding;
 +
 +	if (sides == 2)		// back side
 +		w = ReverseWinding (w);
 +
 +	OutputWinding (w, glview);
 +
 +	if (sides == 2)
 +		FreeWinding(w);
 +}
 +
 +/*
 +=============
 +WriteGLView_r
 +=============
 +*/
 +void WriteGLView_r (node_t *node, FILE *glview)
 +{
 +	portal_t	*p, *nextp;
 +
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		WriteGLView_r (node->children[0], glview);
 +		WriteGLView_r (node->children[1], glview);
 +		return;
 +	}
 +
 +	// write all the portals
 +	for (p=node->portals ; p ; p=nextp)
 +	{
 +		if (p->nodes[0] == node)
 +		{
 +			OutputPortal (p, glview);
 +			nextp = p->next[0];
 +		}
 +		else
 +			nextp = p->next[1];
 +	}
 +}
 +
 +/*
 +=============
 +WriteGLView
 +=============
 +*/
 +void WriteGLView (tree_t *tree, char *source)
 +{
 +	char	name[1024];
 +	FILE	*glview;
 +
 +	c_glfaces = 0;
 +	sprintf (name, "%s%s.gl",outbase, source);
 +	printf ("Writing %s\n", name);
 +
 +	glview = fopen (name, "w");
 +	if (!glview)
 +		Error ("Couldn't open %s", name);
 +	WriteGLView_r (tree->headnode, glview);
 +	fclose (glview);
 +
 +	printf ("%5i c_glfaces\n", c_glfaces);
 +}
 +
 diff --git a/code/bspc/l_bsp_ent.c b/code/bspc/l_bsp_ent.c new file mode 100755 index 0000000..8260179 --- /dev/null +++ b/code/bspc/l_bsp_ent.c @@ -0,0 +1,180 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_mem.h"
 +#include "l_log.h"
 +#include "../botlib/l_script.h"
 +#include "l_bsp_ent.h"
 +
 +#define	MAX_KEY		32
 +#define	MAX_VALUE	1024
 +
 +int num_entities;
 +entity_t	entities[MAX_MAP_ENTITIES];
 +
 +void StripTrailing(char *e)
 +{
 +	char	*s;
 +
 +	s = e + strlen(e)-1;
 +	while (s >= e && *s <= 32)
 +	{
 +		*s = 0;
 +		s--;
 +	}
 +}
 +
 +/*
 +=================
 +ParseEpair
 +=================
 +*/
 +epair_t *ParseEpair(script_t *script)
 +{
 +	epair_t *e;
 +	token_t token;
 +
 +	e = GetMemory(sizeof(epair_t));
 +	memset (e, 0, sizeof(epair_t));
 +	
 +	PS_ExpectAnyToken(script, &token);
 +	StripDoubleQuotes(token.string);
 +	if (strlen(token.string) >= MAX_KEY-1)
 +		Error ("ParseEpair: token %s too long", token.string);
 +	e->key = copystring(token.string);
 +	PS_ExpectAnyToken(script, &token);
 +	StripDoubleQuotes(token.string);
 +	if (strlen(token.string) >= MAX_VALUE-1)
 +		Error ("ParseEpair: token %s too long", token.string);
 +	e->value = copystring(token.string);
 +
 +	// strip trailing spaces
 +	StripTrailing(e->key);
 +	StripTrailing(e->value);
 +
 +	return e;
 +} //end of the function ParseEpair
 +
 +
 +/*
 +================
 +ParseEntity
 +================
 +*/
 +qboolean	ParseEntity(script_t *script)
 +{
 +	epair_t *e;
 +	entity_t	*mapent;
 +	token_t token;
 +
 +	if (!PS_ReadToken(script, &token))
 +		return false;
 +
 +	if (strcmp(token.string, "{"))
 +		Error ("ParseEntity: { not found");
 +	
 +	if (num_entities == MAX_MAP_ENTITIES)
 +		Error ("num_entities == MAX_MAP_ENTITIES");
 +
 +	mapent = &entities[num_entities];
 +	num_entities++;
 +
 +	do
 +	{
 +		if (!PS_ReadToken(script, &token))
 +			Error ("ParseEntity: EOF without closing brace");
 +		if (!strcmp(token.string, "}") )
 +			break;
 +		PS_UnreadLastToken(script);
 +		e = ParseEpair(script);
 +		e->next = mapent->epairs;
 +		mapent->epairs = e;
 +	} while (1);
 +	
 +	return true;
 +} //end of the function ParseEntity
 +
 +void PrintEntity (entity_t *ent)
 +{
 +	epair_t	*ep;
 +	
 +	printf ("------- entity %p -------\n", ent);
 +	for (ep=ent->epairs ; ep ; ep=ep->next)
 +	{
 +		printf ("%s = %s\n", ep->key, ep->value);
 +	}
 +
 +}
 +
 +void 	SetKeyValue (entity_t *ent, char *key, char *value)
 +{
 +	epair_t	*ep;
 +	
 +	for (ep=ent->epairs ; ep ; ep=ep->next)
 +		if (!strcmp (ep->key, key) )
 +		{
 +			FreeMemory(ep->value);
 +			ep->value = copystring(value);
 +			return;
 +		}
 +	ep = GetMemory(sizeof(*ep));
 +	ep->next = ent->epairs;
 +	ent->epairs = ep;
 +	ep->key = copystring(key);
 +	ep->value = copystring(value);
 +}
 +
 +char 	*ValueForKey (entity_t *ent, char *key)
 +{
 +	epair_t	*ep;
 +	
 +	for (ep=ent->epairs ; ep ; ep=ep->next)
 +		if (!strcmp (ep->key, key) )
 +			return ep->value;
 +	return "";
 +}
 +
 +vec_t	FloatForKey (entity_t *ent, char *key)
 +{
 +	char	*k;
 +	
 +	k = ValueForKey (ent, key);
 +	return atof(k);
 +}
 +
 +void 	GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
 +{
 +	char	*k;
 +	double	v1, v2, v3;
 +
 +	k = ValueForKey (ent, key);
 +// scanf into doubles, then assign, so it is vec_t size independent
 +	v1 = v2 = v3 = 0;
 +	sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
 +	vec[0] = v1;
 +	vec[1] = v2;
 +	vec[2] = v3;
 +}
 +
 +
 diff --git a/code/bspc/l_bsp_ent.h b/code/bspc/l_bsp_ent.h new file mode 100755 index 0000000..05dd162 --- /dev/null +++ b/code/bspc/l_bsp_ent.h @@ -0,0 +1,58 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#ifndef MAX_MAP_ENTITIES
 +#define	MAX_MAP_ENTITIES	2048
 +#endif
 +
 +typedef struct epair_s
 +{
 +	struct epair_s	*next;
 +	char	*key;
 +	char	*value;
 +} epair_t;
 +
 +typedef struct
 +{
 +	vec3_t		origin;
 +	int			firstbrush;
 +	int			numbrushes;
 +	epair_t		*epairs;
 +	// only valid for func_areaportals
 +	int			areaportalnum;
 +	int			portalareas[2];
 +	int			modelnum;	//for bsp 2 map conversion
 +   qboolean		wasdetail;	//for SIN
 +} entity_t;
 +
 +extern	int num_entities;
 +extern	entity_t entities[MAX_MAP_ENTITIES];
 +
 +void StripTrailing(char *e);
 +void SetKeyValue(entity_t *ent, char *key, char *value);
 +char *ValueForKey(entity_t *ent, char *key); // will return "" if not present
 +vec_t FloatForKey(entity_t *ent, char *key);
 +void GetVectorForKey(entity_t *ent, char *key, vec3_t vec);
 +qboolean ParseEntity(script_t *script);
 +epair_t *ParseEpair(script_t *script);
 +void PrintEntity(entity_t *ent);
 +
 diff --git a/code/bspc/l_bsp_hl.c b/code/bspc/l_bsp_hl.c new file mode 100755 index 0000000..5b922b5 --- /dev/null +++ b/code/bspc/l_bsp_hl.c @@ -0,0 +1,888 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_mem.h"
 +#include "l_log.h"
 +#include "../botlib/l_script.h"
 +#include "l_bsp_hl.h"
 +#include "l_bsp_ent.h"
 +
 +//=============================================================================
 +
 +int				hl_nummodels;
 +hl_dmodel_t		*hl_dmodels;//[HL_MAX_MAP_MODELS];
 +int				hl_dmodels_checksum;
 +
 +int				hl_visdatasize;
 +byte				*hl_dvisdata;//[HL_MAX_MAP_VISIBILITY];
 +int				hl_dvisdata_checksum;
 +
 +int				hl_lightdatasize;
 +byte				*hl_dlightdata;//[HL_MAX_MAP_LIGHTING];
 +int				hl_dlightdata_checksum;
 +
 +int				hl_texdatasize;
 +byte				*hl_dtexdata;//[HL_MAX_MAP_MIPTEX]; // (dmiptexlump_t)
 +int				hl_dtexdata_checksum;
 +
 +int				hl_entdatasize;
 +char				*hl_dentdata;//[HL_MAX_MAP_ENTSTRING];
 +int				hl_dentdata_checksum;
 +
 +int				hl_numleafs;
 +hl_dleaf_t		*hl_dleafs;//[HL_MAX_MAP_LEAFS];
 +int				hl_dleafs_checksum;
 +
 +int				hl_numplanes;
 +hl_dplane_t		*hl_dplanes;//[HL_MAX_MAP_PLANES];
 +int				hl_dplanes_checksum;
 +
 +int				hl_numvertexes;
 +hl_dvertex_t	*hl_dvertexes;//[HL_MAX_MAP_VERTS];
 +int				hl_dvertexes_checksum;
 +
 +int				hl_numnodes;
 +hl_dnode_t		*hl_dnodes;//[HL_MAX_MAP_NODES];
 +int				hl_dnodes_checksum;
 +
 +int				hl_numtexinfo;
 +hl_texinfo_t	*hl_texinfo;//[HL_MAX_MAP_TEXINFO];
 +int				hl_texinfo_checksum;
 +
 +int				hl_numfaces;
 +hl_dface_t		*hl_dfaces;//[HL_MAX_MAP_FACES];
 +int				hl_dfaces_checksum;
 +
 +int				hl_numclipnodes;
 +hl_dclipnode_t	*hl_dclipnodes;//[HL_MAX_MAP_CLIPNODES];
 +int				hl_dclipnodes_checksum;
 +
 +int				hl_numedges;
 +hl_dedge_t		*hl_dedges;//[HL_MAX_MAP_EDGES];
 +int				hl_dedges_checksum;
 +
 +int				hl_nummarksurfaces;
 +unsigned short	*hl_dmarksurfaces;//[HL_MAX_MAP_MARKSURFACES];
 +int				hl_dmarksurfaces_checksum;
 +
 +int				hl_numsurfedges;
 +int				*hl_dsurfedges;//[HL_MAX_MAP_SURFEDGES];
 +int				hl_dsurfedges_checksum;
 +
 +//int				num_entities;
 +//entity_t			entities[HL_MAX_MAP_ENTITIES];
 +
 +
 +//#ifdef //ME
 +
 +int hl_bspallocated = false;
 +int hl_allocatedbspmem = 0;
 +
 +void HL_AllocMaxBSP(void)
 +{
 +	//models
 +	hl_nummodels = 0;
 +	hl_dmodels = (hl_dmodel_t *) GetMemory(HL_MAX_MAP_MODELS * sizeof(hl_dmodel_t));
 +	hl_allocatedbspmem = HL_MAX_MAP_MODELS * sizeof(hl_dmodel_t);
 +	//visibility
 +	hl_visdatasize = 0;
 +	hl_dvisdata = (byte *) GetMemory(HL_MAX_MAP_VISIBILITY * sizeof(byte));
 +	hl_allocatedbspmem += HL_MAX_MAP_VISIBILITY * sizeof(byte);
 +	//light data
 +	hl_lightdatasize = 0;
 +	hl_dlightdata = (byte *) GetMemory(HL_MAX_MAP_LIGHTING * sizeof(byte));
 +	hl_allocatedbspmem += HL_MAX_MAP_LIGHTING * sizeof(byte);
 +	//texture data
 +	hl_texdatasize = 0;
 +	hl_dtexdata = (byte *) GetMemory(HL_MAX_MAP_MIPTEX * sizeof(byte)); // (dmiptexlump_t)
 +	hl_allocatedbspmem += HL_MAX_MAP_MIPTEX * sizeof(byte);
 +	//entities
 +	hl_entdatasize = 0;
 +	hl_dentdata = (char *) GetMemory(HL_MAX_MAP_ENTSTRING * sizeof(char));
 +	hl_allocatedbspmem += HL_MAX_MAP_ENTSTRING * sizeof(char);
 +	//leaves
 +	hl_numleafs = 0;
 +	hl_dleafs = (hl_dleaf_t *) GetMemory(HL_MAX_MAP_LEAFS * sizeof(hl_dleaf_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_LEAFS * sizeof(hl_dleaf_t);
 +	//planes
 +	hl_numplanes = 0;
 +	hl_dplanes = (hl_dplane_t *) GetMemory(HL_MAX_MAP_PLANES * sizeof(hl_dplane_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_PLANES * sizeof(hl_dplane_t);
 +	//vertexes
 +	hl_numvertexes = 0;
 +	hl_dvertexes = (hl_dvertex_t *) GetMemory(HL_MAX_MAP_VERTS * sizeof(hl_dvertex_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_VERTS * sizeof(hl_dvertex_t);
 +	//nodes
 +	hl_numnodes = 0;
 +	hl_dnodes = (hl_dnode_t *) GetMemory(HL_MAX_MAP_NODES * sizeof(hl_dnode_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_NODES * sizeof(hl_dnode_t);
 +	//texture info
 +	hl_numtexinfo = 0;
 +	hl_texinfo = (hl_texinfo_t *) GetMemory(HL_MAX_MAP_TEXINFO * sizeof(hl_texinfo_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_TEXINFO * sizeof(hl_texinfo_t);
 +	//faces
 +	hl_numfaces = 0;
 +	hl_dfaces = (hl_dface_t *) GetMemory(HL_MAX_MAP_FACES * sizeof(hl_dface_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_FACES * sizeof(hl_dface_t);
 +	//clip nodes
 +	hl_numclipnodes = 0;
 +	hl_dclipnodes = (hl_dclipnode_t *) GetMemory(HL_MAX_MAP_CLIPNODES * sizeof(hl_dclipnode_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_CLIPNODES * sizeof(hl_dclipnode_t);
 +	//edges
 +	hl_numedges = 0;
 +	hl_dedges = (hl_dedge_t *) GetMemory(HL_MAX_MAP_EDGES * sizeof(hl_dedge_t));
 +	hl_allocatedbspmem += HL_MAX_MAP_EDGES, sizeof(hl_dedge_t);
 +	//mark surfaces
 +	hl_nummarksurfaces = 0;
 +	hl_dmarksurfaces = (unsigned short *) GetMemory(HL_MAX_MAP_MARKSURFACES * sizeof(unsigned short));
 +	hl_allocatedbspmem += HL_MAX_MAP_MARKSURFACES * sizeof(unsigned short);
 +	//surface edges
 +	hl_numsurfedges = 0;
 +	hl_dsurfedges = (int *) GetMemory(HL_MAX_MAP_SURFEDGES * sizeof(int));
 +	hl_allocatedbspmem += HL_MAX_MAP_SURFEDGES * sizeof(int);
 +	//print allocated memory
 +	Log_Print("allocated ");
 +	PrintMemorySize(hl_allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +} //end of the function HL_AllocMaxBSP
 +
 +void HL_FreeMaxBSP(void)
 +{
 +	//models
 +	hl_nummodels = 0;
 +	FreeMemory(hl_dmodels);
 +	hl_dmodels = NULL;
 +	//visibility
 +	hl_visdatasize = 0;
 +	FreeMemory(hl_dvisdata);
 +	hl_dvisdata = NULL;
 +	//light data
 +	hl_lightdatasize = 0;
 +	FreeMemory(hl_dlightdata);
 +	hl_dlightdata = NULL;
 +	//texture data
 +	hl_texdatasize = 0;
 +	FreeMemory(hl_dtexdata);
 +	hl_dtexdata = NULL;
 +	//entities
 +	hl_entdatasize = 0;
 +	FreeMemory(hl_dentdata);
 +	hl_dentdata = NULL;
 +	//leaves
 +	hl_numleafs = 0;
 +	FreeMemory(hl_dleafs);
 +	hl_dleafs = NULL;
 +	//planes
 +	hl_numplanes = 0;
 +	FreeMemory(hl_dplanes);
 +	hl_dplanes = NULL;
 +	//vertexes
 +	hl_numvertexes = 0;
 +	FreeMemory(hl_dvertexes);
 +	hl_dvertexes = NULL;
 +	//nodes
 +	hl_numnodes = 0;
 +	FreeMemory(hl_dnodes);
 +	hl_dnodes = NULL;
 +	//texture info
 +	hl_numtexinfo = 0;
 +	FreeMemory(hl_texinfo);
 +	hl_texinfo = NULL;
 +	//faces
 +	hl_numfaces = 0;
 +	FreeMemory(hl_dfaces);
 +	hl_dfaces = NULL;
 +	//clip nodes
 +	hl_numclipnodes = 0;
 +	FreeMemory(hl_dclipnodes);
 +	hl_dclipnodes = NULL;
 +	//edges
 +	hl_numedges = 0;
 +	FreeMemory(hl_dedges);
 +	hl_dedges = NULL;
 +	//mark surfaces
 +	hl_nummarksurfaces = 0;
 +	FreeMemory(hl_dmarksurfaces);
 +	hl_dmarksurfaces = NULL;
 +	//surface edges
 +	hl_numsurfedges = 0;
 +	FreeMemory(hl_dsurfedges);
 +	hl_dsurfedges = NULL;
 +	//
 +	Log_Print("freed ");
 +	PrintMemorySize(hl_allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +	hl_allocatedbspmem = 0;
 +} //end of the function HL_FreeMaxBSP
 +//#endif //ME
 +
 +/*
 +===============
 +FastChecksum
 +===============
 +*/
 +
 +int FastChecksum(void *buffer, int bytes)
 +{
 +	int	checksum = 0;
 +
 +	while( bytes-- )  
 +		checksum = (checksum << 4) ^ *((char *)buffer)++;
 +
 +	return checksum;
 +}
 +
 +/*
 +===============
 +HL_CompressVis
 +===============
 +*/
 +int HL_CompressVis(byte *vis, byte *dest)
 +{
 +	int		j;
 +	int		rep;
 +	int		visrow;
 +	byte	*dest_p;
 +	
 +	dest_p = dest;
 +	visrow = (hl_numleafs + 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;
 +}
 +
 +
 +/*
 +===================
 +HL_DecompressVis
 +===================
 +*/
 +void HL_DecompressVis (byte *in, byte *decompressed)
 +{
 +	int		c;
 +	byte	*out;
 +	int		row;
 +
 +	row = (hl_numleafs+7)>>3;	
 +	out = decompressed;
 +
 +	do
 +	{
 +		if (*in)
 +		{
 +			*out++ = *in++;
 +			continue;
 +		}
 +	
 +		c = in[1];
 +		in += 2;
 +		while (c)
 +		{
 +			*out++ = 0;
 +			c--;
 +		}
 +	} while (out - decompressed < row);
 +}
 +
 +//=============================================================================
 +
 +/*
 +=============
 +HL_SwapBSPFile
 +
 +Byte swaps all data in a bsp file.
 +=============
 +*/
 +void HL_SwapBSPFile (qboolean todisk)
 +{
 +	int i, j, c;
 +	hl_dmodel_t *d;
 +	hl_dmiptexlump_t *mtl;
 +
 +	
 +// models	
 +	for (i = 0; i < hl_nummodels; i++)
 +	{
 +		d = &hl_dmodels[i];
 +
 +		for (j = 0; j < HL_MAX_MAP_HULLS; j++)
 +			d->headnode[j] = LittleLong(d->headnode[j]);
 +
 +		d->visleafs = LittleLong(d->visleafs);
 +		d->firstface = LittleLong(d->firstface);
 +		d->numfaces = LittleLong(d->numfaces);
 +		
 +		for (j = 0; j < 3; j++)
 +		{
 +			d->mins[j] = LittleFloat(d->mins[j]);
 +			d->maxs[j] = LittleFloat(d->maxs[j]);
 +			d->origin[j] = LittleFloat(d->origin[j]);
 +		}
 +	}
 +
 +//
 +// vertexes
 +//
 +	for (i = 0; i < hl_numvertexes; i++)
 +	{
 +		for (j = 0; j < 3; j++)
 +			hl_dvertexes[i].point[j] = LittleFloat (hl_dvertexes[i].point[j]);
 +	}
 +		
 +//
 +// planes
 +//	
 +	for (i=0 ; i<hl_numplanes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			hl_dplanes[i].normal[j] = LittleFloat (hl_dplanes[i].normal[j]);
 +		hl_dplanes[i].dist = LittleFloat (hl_dplanes[i].dist);
 +		hl_dplanes[i].type = LittleLong (hl_dplanes[i].type);
 +	}
 +	
 +//
 +// texinfos
 +//	
 +	for (i=0 ; i<hl_numtexinfo ; i++)
 +	{
 +		for (j=0 ; j<8 ; j++)
 +			hl_texinfo[i].vecs[0][j] = LittleFloat (hl_texinfo[i].vecs[0][j]);
 +		hl_texinfo[i].miptex = LittleLong (hl_texinfo[i].miptex);
 +		hl_texinfo[i].flags = LittleLong (hl_texinfo[i].flags);
 +	}
 +	
 +//
 +// faces
 +//
 +	for (i=0 ; i<hl_numfaces ; i++)
 +	{
 +		hl_dfaces[i].texinfo = LittleShort (hl_dfaces[i].texinfo);
 +		hl_dfaces[i].planenum = LittleShort (hl_dfaces[i].planenum);
 +		hl_dfaces[i].side = LittleShort (hl_dfaces[i].side);
 +		hl_dfaces[i].lightofs = LittleLong (hl_dfaces[i].lightofs);
 +		hl_dfaces[i].firstedge = LittleLong (hl_dfaces[i].firstedge);
 +		hl_dfaces[i].numedges = LittleShort (hl_dfaces[i].numedges);
 +	}
 +
 +//
 +// nodes
 +//
 +	for (i=0 ; i<hl_numnodes ; i++)
 +	{
 +		hl_dnodes[i].planenum = LittleLong (hl_dnodes[i].planenum);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			hl_dnodes[i].mins[j] = LittleShort (hl_dnodes[i].mins[j]);
 +			hl_dnodes[i].maxs[j] = LittleShort (hl_dnodes[i].maxs[j]);
 +		}
 +		hl_dnodes[i].children[0] = LittleShort (hl_dnodes[i].children[0]);
 +		hl_dnodes[i].children[1] = LittleShort (hl_dnodes[i].children[1]);
 +		hl_dnodes[i].firstface = LittleShort (hl_dnodes[i].firstface);
 +		hl_dnodes[i].numfaces = LittleShort (hl_dnodes[i].numfaces);
 +	}
 +
 +//
 +// leafs
 +//
 +	for (i=0 ; i<hl_numleafs ; i++)
 +	{
 +		hl_dleafs[i].contents = LittleLong (hl_dleafs[i].contents);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			hl_dleafs[i].mins[j] = LittleShort (hl_dleafs[i].mins[j]);
 +			hl_dleafs[i].maxs[j] = LittleShort (hl_dleafs[i].maxs[j]);
 +		}
 +
 +		hl_dleafs[i].firstmarksurface = LittleShort (hl_dleafs[i].firstmarksurface);
 +		hl_dleafs[i].nummarksurfaces = LittleShort (hl_dleafs[i].nummarksurfaces);
 +		hl_dleafs[i].visofs = LittleLong (hl_dleafs[i].visofs);
 +	}
 +
 +//
 +// clipnodes
 +//
 +	for (i=0 ; i<hl_numclipnodes ; i++)
 +	{
 +		hl_dclipnodes[i].planenum = LittleLong (hl_dclipnodes[i].planenum);
 +		hl_dclipnodes[i].children[0] = LittleShort (hl_dclipnodes[i].children[0]);
 +		hl_dclipnodes[i].children[1] = LittleShort (hl_dclipnodes[i].children[1]);
 +	}
 +
 +//
 +// miptex
 +//
 +	if (hl_texdatasize)
 +	{
 +		mtl = (hl_dmiptexlump_t *)hl_dtexdata;
 +		if (todisk)
 +			c = mtl->nummiptex;
 +		else
 +			c = LittleLong(mtl->nummiptex);
 +		mtl->nummiptex = LittleLong (mtl->nummiptex);
 +		for (i=0 ; i<c ; i++)
 +			mtl->dataofs[i] = LittleLong(mtl->dataofs[i]);
 +	}
 +	
 +//
 +// marksurfaces
 +//
 +	for (i=0 ; i<hl_nummarksurfaces ; i++)
 +		hl_dmarksurfaces[i] = LittleShort (hl_dmarksurfaces[i]);
 +
 +//
 +// surfedges
 +//
 +	for (i=0 ; i<hl_numsurfedges ; i++)
 +		hl_dsurfedges[i] = LittleLong (hl_dsurfedges[i]);
 +
 +//
 +// edges
 +//
 +	for (i=0 ; i<hl_numedges ; i++)
 +	{
 +		hl_dedges[i].v[0] = LittleShort (hl_dedges[i].v[0]);
 +		hl_dedges[i].v[1] = LittleShort (hl_dedges[i].v[1]);
 +	}
 +} //end of the function HL_SwapBSPFile
 +
 +
 +hl_dheader_t	*hl_header;
 +int				hl_fileLength;
 +
 +int HL_CopyLump (int lump, void *dest, int size, int maxsize)
 +{
 +	int		length, ofs;
 +
 +	length = hl_header->lumps[lump].filelen;
 +	ofs = hl_header->lumps[lump].fileofs;
 +	
 +	if (length % size) {
 +		Error ("LoadBSPFile: odd lump size");
 +	}
 +	// somehow things got out of range
 +	if ((length/size) > maxsize) {
 +		printf("WARNING: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize);
 +		length = maxsize * size;
 +	}
 +	if ( ofs + length > hl_fileLength ) {
 +		printf("WARNING: exceeded file length for lump %d\n", lump);
 +		length = hl_fileLength - ofs;
 +		if ( length <= 0 ) {
 +			return 0;
 +		}
 +	}
 +
 +	memcpy (dest, (byte *)hl_header + ofs, length);
 +
 +	return length / size;
 +}
 +
 +/*
 +=============
 +HL_LoadBSPFile
 +=============
 +*/
 +void	HL_LoadBSPFile (char *filename, int offset, int length)
 +{
 +	int			i;
 +	
 +//
 +// load the file header
 +//
 +	hl_fileLength = LoadFile (filename, (void **)&hl_header, offset, length);
 +
 +// swap the header
 +	for (i=0 ; i< sizeof(hl_dheader_t)/4 ; i++)
 +		((int *)hl_header)[i] = LittleLong ( ((int *)hl_header)[i]);
 +
 +	if (hl_header->version != HL_BSPVERSION)
 +		Error ("%s is version %i, not %i", filename, hl_header->version, HL_BSPVERSION);
 +
 +	hl_nummodels = HL_CopyLump (HL_LUMP_MODELS, hl_dmodels, sizeof(hl_dmodel_t), HL_MAX_MAP_MODELS );
 +	hl_numvertexes = HL_CopyLump (HL_LUMP_VERTEXES, hl_dvertexes, sizeof(hl_dvertex_t), HL_MAX_MAP_VERTS );
 +	hl_numplanes = HL_CopyLump (HL_LUMP_PLANES, hl_dplanes, sizeof(hl_dplane_t), HL_MAX_MAP_PLANES );
 +	hl_numleafs = HL_CopyLump (HL_LUMP_LEAFS, hl_dleafs, sizeof(hl_dleaf_t), HL_MAX_MAP_LEAFS );
 +	hl_numnodes = HL_CopyLump (HL_LUMP_NODES, hl_dnodes, sizeof(hl_dnode_t), HL_MAX_MAP_NODES );
 +	hl_numtexinfo = HL_CopyLump (HL_LUMP_TEXINFO, hl_texinfo, sizeof(hl_texinfo_t), HL_MAX_MAP_TEXINFO );
 +	hl_numclipnodes = HL_CopyLump (HL_LUMP_CLIPNODES, hl_dclipnodes, sizeof(hl_dclipnode_t), HL_MAX_MAP_CLIPNODES );
 +	hl_numfaces = HL_CopyLump (HL_LUMP_FACES, hl_dfaces, sizeof(hl_dface_t), HL_MAX_MAP_FACES );
 +	hl_nummarksurfaces = HL_CopyLump (HL_LUMP_MARKSURFACES, hl_dmarksurfaces, sizeof(hl_dmarksurfaces[0]), HL_MAX_MAP_MARKSURFACES );
 +	hl_numsurfedges = HL_CopyLump (HL_LUMP_SURFEDGES, hl_dsurfedges, sizeof(hl_dsurfedges[0]), HL_MAX_MAP_SURFEDGES );
 +	hl_numedges = HL_CopyLump (HL_LUMP_EDGES, hl_dedges, sizeof(hl_dedge_t), HL_MAX_MAP_EDGES );
 +
 +	hl_texdatasize = HL_CopyLump (HL_LUMP_TEXTURES, hl_dtexdata, 1, HL_MAX_MAP_MIPTEX );
 +	hl_visdatasize = HL_CopyLump (HL_LUMP_VISIBILITY, hl_dvisdata, 1, HL_MAX_MAP_VISIBILITY );
 +	hl_lightdatasize = HL_CopyLump (HL_LUMP_LIGHTING, hl_dlightdata, 1, HL_MAX_MAP_LIGHTING );
 +	hl_entdatasize = HL_CopyLump (HL_LUMP_ENTITIES, hl_dentdata, 1, HL_MAX_MAP_ENTSTRING );
 +
 +	FreeMemory(hl_header);		// everything has been copied out
 +		
 +//
 +// swap everything
 +//	
 +	HL_SwapBSPFile (false);
 +
 +	hl_dmodels_checksum = FastChecksum( hl_dmodels, hl_nummodels*sizeof(hl_dmodels[0]) );
 +	hl_dvertexes_checksum = FastChecksum( hl_dvertexes, hl_numvertexes*sizeof(hl_dvertexes[0]) );
 +	hl_dplanes_checksum = FastChecksum( hl_dplanes, hl_numplanes*sizeof(hl_dplanes[0]) );
 +	hl_dleafs_checksum = FastChecksum( hl_dleafs, hl_numleafs*sizeof(hl_dleafs[0]) );
 +	hl_dnodes_checksum = FastChecksum( hl_dnodes, hl_numnodes*sizeof(hl_dnodes[0]) );
 +	hl_texinfo_checksum = FastChecksum( hl_texinfo, hl_numtexinfo*sizeof(hl_texinfo[0]) );
 +	hl_dclipnodes_checksum = FastChecksum( hl_dclipnodes, hl_numclipnodes*sizeof(hl_dclipnodes[0]) );
 +	hl_dfaces_checksum = FastChecksum( hl_dfaces, hl_numfaces*sizeof(hl_dfaces[0]) );
 +	hl_dmarksurfaces_checksum = FastChecksum( hl_dmarksurfaces, hl_nummarksurfaces*sizeof(hl_dmarksurfaces[0]) );
 +	hl_dsurfedges_checksum = FastChecksum( hl_dsurfedges, hl_numsurfedges*sizeof(hl_dsurfedges[0]) );
 +	hl_dedges_checksum = FastChecksum( hl_dedges, hl_numedges*sizeof(hl_dedges[0]) );
 +	hl_dtexdata_checksum = FastChecksum( hl_dtexdata, hl_numedges*sizeof(hl_dtexdata[0]) );
 +	hl_dvisdata_checksum = FastChecksum( hl_dvisdata, hl_visdatasize*sizeof(hl_dvisdata[0]) );
 +	hl_dlightdata_checksum = FastChecksum( hl_dlightdata, hl_lightdatasize*sizeof(hl_dlightdata[0]) );
 +	hl_dentdata_checksum = FastChecksum( hl_dentdata, hl_entdatasize*sizeof(hl_dentdata[0]) );
 +
 +}
 +
 +//============================================================================
 +
 +FILE		*wadfile;
 +hl_dheader_t	outheader;
 +
 +void HL_AddLump (int lumpnum, void *data, int len)
 +{
 +	hl_lump_t *lump;
 +
 +	lump = &hl_header->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong( ftell(wadfile) );
 +	lump->filelen = LittleLong(len);
 +	SafeWrite (wadfile, data, (len+3)&~3);
 +}
 +
 +/*
 +=============
 +HL_WriteBSPFile
 +
 +Swaps the bsp file in place, so it should not be referenced again
 +=============
 +*/
 +void HL_WriteBSPFile (char *filename)
 +{		
 +	hl_header = &outheader;
 +	memset (hl_header, 0, sizeof(hl_dheader_t));
 +	
 +	HL_SwapBSPFile (true);
 +
 +	hl_header->version = LittleLong (HL_BSPVERSION);
 +	
 +	wadfile = SafeOpenWrite (filename);
 +	SafeWrite (wadfile, hl_header, sizeof(hl_dheader_t));	// overwritten later
 +
 +	HL_AddLump (HL_LUMP_PLANES, hl_dplanes, hl_numplanes*sizeof(hl_dplane_t));
 +	HL_AddLump (HL_LUMP_LEAFS, hl_dleafs, hl_numleafs*sizeof(hl_dleaf_t));
 +	HL_AddLump (HL_LUMP_VERTEXES, hl_dvertexes, hl_numvertexes*sizeof(hl_dvertex_t));
 +	HL_AddLump (HL_LUMP_NODES, hl_dnodes, hl_numnodes*sizeof(hl_dnode_t));
 +	HL_AddLump (HL_LUMP_TEXINFO, hl_texinfo, hl_numtexinfo*sizeof(hl_texinfo_t));
 +	HL_AddLump (HL_LUMP_FACES, hl_dfaces, hl_numfaces*sizeof(hl_dface_t));
 +	HL_AddLump (HL_LUMP_CLIPNODES, hl_dclipnodes, hl_numclipnodes*sizeof(hl_dclipnode_t));
 +	HL_AddLump (HL_LUMP_MARKSURFACES, hl_dmarksurfaces, hl_nummarksurfaces*sizeof(hl_dmarksurfaces[0]));
 +	HL_AddLump (HL_LUMP_SURFEDGES, hl_dsurfedges, hl_numsurfedges*sizeof(hl_dsurfedges[0]));
 +	HL_AddLump (HL_LUMP_EDGES, hl_dedges, hl_numedges*sizeof(hl_dedge_t));
 +	HL_AddLump (HL_LUMP_MODELS, hl_dmodels, hl_nummodels*sizeof(hl_dmodel_t));
 +
 +	HL_AddLump (HL_LUMP_LIGHTING, hl_dlightdata, hl_lightdatasize);
 +	HL_AddLump (HL_LUMP_VISIBILITY, hl_dvisdata, hl_visdatasize);
 +	HL_AddLump (HL_LUMP_ENTITIES, hl_dentdata, hl_entdatasize);
 +	HL_AddLump (HL_LUMP_TEXTURES, hl_dtexdata, hl_texdatasize);
 +	
 +	fseek (wadfile, 0, SEEK_SET);
 +	SafeWrite (wadfile, hl_header, sizeof(hl_dheader_t));
 +	fclose (wadfile);	
 +}
 +
 +//============================================================================
 +
 +#define ENTRIES(a)		(sizeof(a)/sizeof(*(a)))
 +#define ENTRYSIZE(a)	(sizeof(*(a)))
 +
 +ArrayUsage( char *szItem, int items, int maxitems, int itemsize )
 +{
 +	float	percentage = maxitems ? items * 100.0 / maxitems : 0.0;
 +
 +   qprintf("%-12s  %7i/%-7i  %7i/%-7i  (%4.1f%%)", 
 +				szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage );
 +	if ( percentage > 80.0 )
 +		qprintf( "VERY FULL!\n" );
 +	else if ( percentage > 95.0 )
 +		qprintf( "SIZE DANGER!\n" );
 +	else if ( percentage > 99.9 )
 +		qprintf( "SIZE OVERFLOW!!!\n" );
 +	else
 +		qprintf( "\n" );
 +	return items * itemsize;
 +}
 +
 +GlobUsage( char *szItem, int itemstorage, int maxstorage )
 +{
 +	float	percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0;
 +
 +	qprintf("%-12s     [variable]    %7i/%-7i  (%4.1f%%)", 
 +				szItem, itemstorage, maxstorage, percentage );
 +	if ( percentage > 80.0 )
 +		qprintf( "VERY FULL!\n" );
 +	else if ( percentage > 95.0 )
 +		qprintf( "SIZE DANGER!\n" );
 +	else if ( percentage > 99.9 )
 +		qprintf( "SIZE OVERFLOW!!!\n" );
 +	else
 +		qprintf( "\n" );
 +	return itemstorage;
 +}
 +
 +/*
 +=============
 +HL_PrintBSPFileSizes
 +
 +Dumps info about current file
 +=============
 +*/
 +void HL_PrintBSPFileSizes(void)
 +{
 +	int	numtextures = hl_texdatasize ? ((hl_dmiptexlump_t*)hl_dtexdata)->nummiptex : 0;
 +	int	totalmemory = 0;
 +
 +	qprintf("\n");
 +	qprintf("Object names  Objects/Maxobjs  Memory / Maxmem  Fullness\n" );
 +	qprintf("------------  ---------------  ---------------  --------\n" );
 +
 +	totalmemory += ArrayUsage( "models",		hl_nummodels,		ENTRIES(hl_dmodels),		ENTRYSIZE(hl_dmodels) );
 +	totalmemory += ArrayUsage( "planes",		hl_numplanes,		ENTRIES(hl_dplanes),		ENTRYSIZE(hl_dplanes) );
 +	totalmemory += ArrayUsage( "vertexes",		hl_numvertexes,	ENTRIES(hl_dvertexes),	ENTRYSIZE(hl_dvertexes) );
 +	totalmemory += ArrayUsage( "nodes",			hl_numnodes,		ENTRIES(hl_dnodes),		ENTRYSIZE(hl_dnodes) );
 +	totalmemory += ArrayUsage( "texinfos",		hl_numtexinfo,		ENTRIES(hl_texinfo),		ENTRYSIZE(hl_texinfo) );
 +	totalmemory += ArrayUsage( "faces",			hl_numfaces,		ENTRIES(hl_dfaces),		ENTRYSIZE(hl_dfaces) );
 +	totalmemory += ArrayUsage( "clipnodes",	hl_numclipnodes,	ENTRIES(hl_dclipnodes),	ENTRYSIZE(hl_dclipnodes) );
 +	totalmemory += ArrayUsage( "leaves",		hl_numleafs,		ENTRIES(hl_dleafs),		ENTRYSIZE(hl_dleafs) );
 +	totalmemory += ArrayUsage( "marksurfaces",hl_nummarksurfaces,ENTRIES(hl_dmarksurfaces),ENTRYSIZE(hl_dmarksurfaces) );
 +	totalmemory += ArrayUsage( "surfedges",	hl_numsurfedges,	ENTRIES(hl_dsurfedges),	ENTRYSIZE(hl_dsurfedges) );
 +	totalmemory += ArrayUsage( "edges",			hl_numedges,		ENTRIES(hl_dedges),		ENTRYSIZE(hl_dedges) );
 +
 +	totalmemory += GlobUsage( "texdata",		hl_texdatasize,	sizeof(hl_dtexdata) );
 +	totalmemory += GlobUsage( "lightdata",		hl_lightdatasize,	sizeof(hl_dlightdata) );
 +	totalmemory += GlobUsage( "visdata",		hl_visdatasize,	sizeof(hl_dvisdata) );
 +	totalmemory += GlobUsage( "entdata",		hl_entdatasize,	sizeof(hl_dentdata) );
 +
 +	qprintf( "=== Total BSP file data space used: %d bytes ===\n\n", totalmemory );
 +}
 +
 +
 +
 +/*
 +=================
 +ParseEpair
 +=================
 +* /
 +epair_t *ParseEpair (void)
 +{
 +	epair_t	*e;
 +	
 +	e = malloc (sizeof(epair_t));
 +	memset (e, 0, sizeof(epair_t));
 +	
 +	if (strlen(token) >= MAX_KEY-1)
 +		Error ("ParseEpar: token too long");
 +	e->key = copystring(token);
 +	GetToken (false);
 +	if (strlen(token) >= MAX_VALUE-1)
 +		Error ("ParseEpar: token too long");
 +	e->value = copystring(token);
 +
 +	return e;
 +} //*/
 +
 +
 +/*
 +================
 +ParseEntity
 +================
 +* /
 +qboolean	ParseEntity (void)
 +{
 +	epair_t		*e;
 +	entity_t	*mapent;
 +
 +	if (!GetToken (true))
 +		return false;
 +
 +	if (strcmp (token, "{") )
 +		Error ("ParseEntity: { not found");
 +	
 +	if (num_entities == HL_MAX_MAP_ENTITIES)
 +		Error ("num_entities == HL_MAX_MAP_ENTITIES");
 +
 +	mapent = &entities[num_entities];
 +	num_entities++;
 +
 +	do
 +	{
 +		if (!GetToken (true))
 +			Error ("ParseEntity: EOF without closing brace");
 +		if (!strcmp (token, "}") )
 +			break;
 +		e = ParseEpair ();
 +		e->next = mapent->epairs;
 +		mapent->epairs = e;
 +	} while (1);
 +	
 +	return true;
 +} //*/
 +
 +/*
 +================
 +ParseEntities
 +
 +Parses the dentdata string into entities
 +================
 +*/
 +void HL_ParseEntities (void)
 +{
 +	script_t *script;
 +
 +	num_entities = 0;
 +	script = LoadScriptMemory(hl_dentdata, hl_entdatasize, "*Half-Life bsp file");
 +	SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES |
 +									SCFL_NOSTRINGESCAPECHARS);
 +
 +	while(ParseEntity(script))
 +	{
 +	} //end while
 +
 +	FreeScript(script);
 +} //end of the function HL_ParseEntities
 +
 +
 +/*
 +================
 +UnparseEntities
 +
 +Generates the dentdata string from all the entities
 +================
 +*/
 +void HL_UnparseEntities (void)
 +{
 +	char *buf, *end;
 +	epair_t *ep;
 +	char line[2048];
 +	int i;
 +	
 +	buf = hl_dentdata;
 +	end = buf;
 +	*end = 0;
 +	
 +	for (i=0 ; i<num_entities ; i++)
 +	{
 +		ep = entities[i].epairs;
 +		if (!ep)
 +			continue;	// ent got removed
 +		
 +		strcat (end,"{\n");
 +		end += 2;
 +				
 +		for (ep = entities[i].epairs ; ep ; ep=ep->next)
 +		{
 +			sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
 +			strcat (end, line);
 +			end += strlen(line);
 +		}
 +		strcat (end,"}\n");
 +		end += 2;
 +
 +		if (end > buf + HL_MAX_MAP_ENTSTRING)
 +			Error ("Entity text too long");
 +	}
 +	hl_entdatasize = end - buf + 1;
 +} //end of the function HL_UnparseEntities
 +
 +
 +/*
 +void 	SetKeyValue (entity_t *ent, char *key, char *value)
 +{
 +	epair_t	*ep;
 +	
 +	for (ep=ent->epairs ; ep ; ep=ep->next)
 +		if (!strcmp (ep->key, key) )
 +		{
 +			free (ep->value);
 +			ep->value = copystring(value);
 +			return;
 +		}
 +	ep = malloc (sizeof(*ep));
 +	ep->next = ent->epairs;
 +	ent->epairs = ep;
 +	ep->key = copystring(key);
 +	ep->value = copystring(value);
 +}
 +
 +char 	*ValueForKey (entity_t *ent, char *key)
 +{
 +	epair_t	*ep;
 +	
 +	for (ep=ent->epairs ; ep ; ep=ep->next)
 +		if (!strcmp (ep->key, key) )
 +			return ep->value;
 +	return "";
 +}
 +
 +vec_t	FloatForKey (entity_t *ent, char *key)
 +{
 +	char	*k;
 +	
 +	k = ValueForKey (ent, key);
 +	return atof(k);
 +}
 +
 +void 	GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
 +{
 +	char	*k;
 +	double	v1, v2, v3;
 +
 +	k = ValueForKey (ent, key);
 +// scanf into doubles, then assign, so it is vec_t size independent
 +	v1 = v2 = v3 = 0;
 +	sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
 +	vec[0] = v1;
 +	vec[1] = v2;
 +	vec[2] = v3;
 +} //*/
 diff --git a/code/bspc/l_bsp_hl.h b/code/bspc/l_bsp_hl.h new file mode 100755 index 0000000..0089b1e --- /dev/null +++ b/code/bspc/l_bsp_hl.h @@ -0,0 +1,314 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +// upper design bounds
 +
 +#define	HL_MAX_MAP_HULLS			4
 +
 +#define	HL_MAX_MAP_MODELS			400
 +#define	HL_MAX_MAP_BRUSHES		4096
 +#define	HL_MAX_MAP_ENTITIES		1024
 +#define	HL_MAX_MAP_ENTSTRING		(128*1024)
 +
 +#define	HL_MAX_MAP_PLANES			32767
 +#define	HL_MAX_MAP_NODES			32767		// because negative shorts are contents
 +#define	HL_MAX_MAP_CLIPNODES		32767		//
 +#define	HL_MAX_MAP_LEAFS			8192
 +#define	HL_MAX_MAP_VERTS			65535
 +#define	HL_MAX_MAP_FACES			65535
 +#define	HL_MAX_MAP_MARKSURFACES 65535
 +#define	HL_MAX_MAP_TEXINFO		8192
 +#define	HL_MAX_MAP_EDGES			256000
 +#define	HL_MAX_MAP_SURFEDGES		512000
 +#define	HL_MAX_MAP_TEXTURES		512
 +#define	HL_MAX_MAP_MIPTEX			0x200000
 +#define	HL_MAX_MAP_LIGHTING		0x200000
 +#define	HL_MAX_MAP_VISIBILITY	0x200000
 +
 +#define	HL_MAX_MAP_PORTALS		65536
 +
 +// key / value pair sizes
 +
 +#define	MAX_KEY		32
 +#define	MAX_VALUE	1024
 +
 +//=============================================================================
 +
 +
 +#define HL_BSPVERSION	30
 +#define HL_TOOLVERSION	2
 +
 +
 +typedef struct
 +{
 +	int		fileofs, filelen;
 +} hl_lump_t;
 +
 +#define	HL_LUMP_ENTITIES	0
 +#define	HL_LUMP_PLANES		1
 +#define	HL_LUMP_TEXTURES	2
 +#define	HL_LUMP_VERTEXES	3
 +#define	HL_LUMP_VISIBILITY	4
 +#define	HL_LUMP_NODES		5
 +#define	HL_LUMP_TEXINFO	6
 +#define	HL_LUMP_FACES		7
 +#define	HL_LUMP_LIGHTING	8
 +#define	HL_LUMP_CLIPNODES	9
 +#define	HL_LUMP_LEAFS		10
 +#define	HL_LUMP_MARKSURFACES 11
 +#define	HL_LUMP_EDGES		12
 +#define	HL_LUMP_SURFEDGES	13
 +#define	HL_LUMP_MODELS		14
 +
 +#define	HL_HEADER_LUMPS	15
 +
 +typedef struct
 +{
 +	float		mins[3], maxs[3];
 +	float		origin[3];
 +	int		headnode[HL_MAX_MAP_HULLS];
 +	int		visleafs;		// not including the solid leaf 0
 +	int		firstface, numfaces;
 +} hl_dmodel_t;
 +
 +typedef struct
 +{
 +	int			version;	
 +	hl_lump_t	lumps[HL_HEADER_LUMPS];
 +} hl_dheader_t;
 +
 +typedef struct
 +{
 +	int			nummiptex;
 +	int			dataofs[4];		// [nummiptex]
 +} hl_dmiptexlump_t;
 +
 +#define	MIPLEVELS	4
 +typedef struct hl_miptex_s
 +{
 +	char		name[16];
 +	unsigned	width, height;
 +	unsigned	offsets[MIPLEVELS];		// four mip maps stored
 +} hl_miptex_t;
 +
 +
 +typedef struct
 +{
 +	float	point[3];
 +} hl_dvertex_t;
 +
 +
 +// 0-2 are axial planes
 +#define	PLANE_X			0
 +#define	PLANE_Y			1
 +#define	PLANE_Z			2
 +
 +// 3-5 are non-axial planes snapped to the nearest
 +#define	PLANE_ANYX		3
 +#define	PLANE_ANYY		4
 +#define	PLANE_ANYZ		5
 +
 +typedef struct
 +{
 +	float	normal[3];
 +	float	dist;
 +	int	type;		// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
 +} hl_dplane_t;
 +
 +
 +
 +#define	HL_CONTENTS_EMPTY				-1
 +#define	HL_CONTENTS_SOLID				-2
 +#define	HL_CONTENTS_WATER				-3
 +#define	HL_CONTENTS_SLIME				-4
 +#define	HL_CONTENTS_LAVA				-5
 +#define	HL_CONTENTS_SKY				-6
 +#define	HL_CONTENTS_ORIGIN			-7		// removed at csg time
 +#define	HL_CONTENTS_CLIP				-8		// changed to contents_solid
 +
 +#define	HL_CONTENTS_CURRENT_0		-9
 +#define	HL_CONTENTS_CURRENT_90		-10
 +#define	HL_CONTENTS_CURRENT_180		-11
 +#define	HL_CONTENTS_CURRENT_270		-12
 +#define	HL_CONTENTS_CURRENT_UP		-13
 +#define	HL_CONTENTS_CURRENT_DOWN	-14
 +
 +#define HL_CONTENTS_TRANSLUCENT		-15
 +
 +// !!! if this is changed, it must be changed in asm_i386.h too !!!
 +typedef struct
 +{
 +	int		planenum;
 +	short		children[2];	// negative numbers are -(leafs+1), not nodes
 +	short		mins[3];		// for sphere culling
 +	short		maxs[3];
 +	unsigned short	firstface;
 +	unsigned short	numfaces;	// counting both sides
 +} hl_dnode_t;
 +
 +typedef struct
 +{
 +	int		planenum;
 +	short		children[2];	// negative numbers are contents
 +} hl_dclipnode_t;
 +
 +
 +typedef struct hl_texinfo_s
 +{
 +	float		vecs[2][4];		// [s/t][xyz offset]
 +	int		miptex;
 +	int		flags;
 +} hl_texinfo_t;
 +#define	TEX_SPECIAL		1		// sky or slime, no lightmap or 256 subdivision
 +
 +// note that edge 0 is never used, because negative edge nums are used for
 +// counterclockwise use of the edge in a face
 +typedef struct
 +{
 +	unsigned short	v[2];		// vertex numbers
 +} hl_dedge_t;
 +
 +#define	MAXLIGHTMAPS	4
 +typedef struct
 +{
 +	short		planenum;
 +	short		side;
 +
 +	int		firstedge;		// we must support > 64k edges
 +	short		numedges;	
 +	short		texinfo;
 +
 +// lighting info
 +	byte		styles[MAXLIGHTMAPS];
 +	int		lightofs;		// start of [numstyles*surfsize] samples
 +} hl_dface_t;
 +
 +
 +#define	AMBIENT_WATER	0
 +#define	AMBIENT_SKY		1
 +#define	AMBIENT_SLIME	2
 +#define	AMBIENT_LAVA	3
 +
 +#define	NUM_AMBIENTS			4		// automatic ambient sounds
 +
 +// leaf 0 is the generic HL_CONTENTS_SOLID leaf, used for all solid areas
 +// all other leafs need visibility info
 +typedef struct
 +{
 +	int			contents;
 +	int			visofs;				// -1 = no visibility info
 +
 +	short			mins[3];			// for frustum culling
 +	short			maxs[3];
 +
 +	unsigned short		firstmarksurface;
 +	unsigned short		nummarksurfaces;
 +
 +	byte		ambient_level[NUM_AMBIENTS];
 +} hl_dleaf_t;
 +
 +
 +//============================================================================
 +
 +#ifndef QUAKE_GAME
 +
 +#define	ANGLE_UP	-1
 +#define	ANGLE_DOWN	-2
 +
 +
 +// the utilities get to be lazy and just use large static arrays
 +
 +extern	int				hl_nummodels;
 +extern	hl_dmodel_t		*hl_dmodels;//[MAX_MAP_MODELS];
 +extern	int				hl_dmodels_checksum;
 +
 +extern	int				hl_visdatasize;
 +extern	byte				*hl_dvisdata;//[MAX_MAP_VISIBILITY];
 +extern	int				hl_dvisdata_checksum;
 +
 +extern	int				hl_lightdatasize;
 +extern	byte				*hl_dlightdata;//[MAX_MAP_LIGHTING];
 +extern	int				hl_dlightdata_checksum;
 +
 +extern	int				hl_texdatasize;
 +extern	byte				*hl_dtexdata;//[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
 +extern	int				hl_dtexdata_checksum;
 +
 +extern	int				hl_entdatasize;
 +extern	char				*hl_dentdata;//[MAX_MAP_ENTSTRING];
 +extern	int				hl_dentdata_checksum;
 +
 +extern	int				hl_numleafs;
 +extern	hl_dleaf_t		*hl_dleafs;//[MAX_MAP_LEAFS];
 +extern	int				hl_dleafs_checksum;
 +
 +extern	int				hl_numplanes;
 +extern	hl_dplane_t		*hl_dplanes;//[MAX_MAP_PLANES];
 +extern	int				hl_dplanes_checksum;
 +
 +extern	int				hl_numvertexes;
 +extern	hl_dvertex_t	*hl_dvertexes;//[MAX_MAP_VERTS];
 +extern	int				hl_dvertexes_checksum;
 +
 +extern	int				hl_numnodes;
 +extern	hl_dnode_t		*hl_dnodes;//[MAX_MAP_NODES];
 +extern	int				hl_dnodes_checksum;
 +
 +extern	int				hl_numtexinfo;
 +extern	hl_texinfo_t	*hl_texinfo;//[MAX_MAP_TEXINFO];
 +extern	int				hl_texinfo_checksum;
 +
 +extern	int				hl_numfaces;
 +extern	hl_dface_t		*hl_dfaces;//[MAX_MAP_FACES];
 +extern	int				hl_dfaces_checksum;
 +
 +extern	int				hl_numclipnodes;
 +extern	hl_dclipnode_t	*hl_dclipnodes;//[MAX_MAP_CLIPNODES];
 +extern	int				hl_dclipnodes_checksum;
 +
 +extern	int				hl_numedges;
 +extern	hl_dedge_t		*hl_dedges;//[MAX_MAP_EDGES];
 +extern	int				hl_dedges_checksum;
 +
 +extern	int				hl_nummarksurfaces;
 +extern	unsigned short	*hl_dmarksurfaces;//[MAX_MAP_MARKSURFACES];
 +extern	int				hl_dmarksurfaces_checksum;
 +
 +extern	int				hl_numsurfedges;
 +extern	int				*hl_dsurfedges;//[MAX_MAP_SURFEDGES];
 +extern	int				hl_dsurfedges_checksum;
 +
 +int FastChecksum(void *buffer, int bytes);
 +
 +void HL_AllocMaxBSP(void);
 +void HL_FreeMaxBSP(void);
 +
 +void HL_DecompressVis(byte *in, byte *decompressed);
 +int HL_CompressVis(byte *vis, byte *dest);
 +
 +void HL_LoadBSPFile(char *filename, int offset, int length);
 +void HL_WriteBSPFile(char *filename);
 +void HL_PrintBSPFileSizes(void);
 +void HL_PrintBSPFileSizes(void);
 +void HL_ParseEntities(void);
 +void HL_UnparseEntities(void);
 +
 +#endif
 diff --git a/code/bspc/l_bsp_q1.c b/code/bspc/l_bsp_q1.c new file mode 100755 index 0000000..c68b5c8 --- /dev/null +++ b/code/bspc/l_bsp_q1.c @@ -0,0 +1,620 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_mem.h"
 +#include "l_log.h"
 +#include "../botlib/l_script.h"
 +#include "l_bsp_q1.h"
 +#include "l_bsp_ent.h"
 +
 +//=============================================================================
 +
 +int				q1_nummodels;
 +q1_dmodel_t		*q1_dmodels;//[MAX_MAP_MODELS];
 +
 +int				q1_visdatasize;
 +byte				*q1_dvisdata;//[MAX_MAP_VISIBILITY];
 +
 +int				q1_lightdatasize;
 +byte				*q1_dlightdata;//[MAX_MAP_LIGHTING];
 +
 +int				q1_texdatasize;
 +byte				*q1_dtexdata;//[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
 +
 +int				q1_entdatasize;
 +char				*q1_dentdata;//[MAX_MAP_ENTSTRING];
 +
 +int				q1_numleafs;
 +q1_dleaf_t		*q1_dleafs;//[MAX_MAP_LEAFS];
 +
 +int				q1_numplanes;
 +q1_dplane_t		*q1_dplanes;//[MAX_MAP_PLANES];
 +
 +int				q1_numvertexes;
 +q1_dvertex_t	*q1_dvertexes;//[MAX_MAP_VERTS];
 +
 +int				q1_numnodes;
 +q1_dnode_t		*q1_dnodes;//[MAX_MAP_NODES];
 +
 +int				q1_numtexinfo;
 +q1_texinfo_t	*q1_texinfo;//[MAX_MAP_TEXINFO];
 +
 +int				q1_numfaces;
 +q1_dface_t		*q1_dfaces;//[MAX_MAP_FACES];
 +
 +int				q1_numclipnodes;
 +q1_dclipnode_t	*q1_dclipnodes;//[MAX_MAP_CLIPNODES];
 +
 +int				q1_numedges;
 +q1_dedge_t		*q1_dedges;//[MAX_MAP_EDGES];
 +
 +int				q1_nummarksurfaces;
 +unsigned short	*q1_dmarksurfaces;//[MAX_MAP_MARKSURFACES];
 +
 +int				q1_numsurfedges;
 +int				*q1_dsurfedges;//[MAX_MAP_SURFEDGES];
 +
 +//=============================================================================
 +
 +int q1_bspallocated = false;
 +int q1_allocatedbspmem = 0;
 +
 +void Q1_AllocMaxBSP(void)
 +{
 +	//models
 +	q1_nummodels = 0;
 +	q1_dmodels = (q1_dmodel_t *) GetMemory(Q1_MAX_MAP_MODELS * sizeof(q1_dmodel_t));
 +	q1_allocatedbspmem = Q1_MAX_MAP_MODELS * sizeof(q1_dmodel_t);
 +	//visibility
 +	q1_visdatasize = 0;
 +	q1_dvisdata = (byte *) GetMemory(Q1_MAX_MAP_VISIBILITY * sizeof(byte));
 +	q1_allocatedbspmem += Q1_MAX_MAP_VISIBILITY * sizeof(byte);
 +	//light data
 +	q1_lightdatasize = 0;
 +	q1_dlightdata = (byte *) GetMemory(Q1_MAX_MAP_LIGHTING * sizeof(byte));
 +	q1_allocatedbspmem += Q1_MAX_MAP_LIGHTING * sizeof(byte);
 +	//texture data
 +	q1_texdatasize = 0;
 +	q1_dtexdata = (byte *) GetMemory(Q1_MAX_MAP_MIPTEX * sizeof(byte)); // (dmiptexlump_t)
 +	q1_allocatedbspmem += Q1_MAX_MAP_MIPTEX * sizeof(byte);
 +	//entities
 +	q1_entdatasize = 0;
 +	q1_dentdata = (char *) GetMemory(Q1_MAX_MAP_ENTSTRING * sizeof(char));
 +	q1_allocatedbspmem += Q1_MAX_MAP_ENTSTRING * sizeof(char);
 +	//leaves
 +	q1_numleafs = 0;
 +	q1_dleafs = (q1_dleaf_t *) GetMemory(Q1_MAX_MAP_LEAFS * sizeof(q1_dleaf_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_LEAFS * sizeof(q1_dleaf_t);
 +	//planes
 +	q1_numplanes = 0;
 +	q1_dplanes = (q1_dplane_t *) GetMemory(Q1_MAX_MAP_PLANES * sizeof(q1_dplane_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_PLANES * sizeof(q1_dplane_t);
 +	//vertexes
 +	q1_numvertexes = 0;
 +	q1_dvertexes = (q1_dvertex_t *) GetMemory(Q1_MAX_MAP_VERTS * sizeof(q1_dvertex_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_VERTS * sizeof(q1_dvertex_t);
 +	//nodes
 +	q1_numnodes = 0;
 +	q1_dnodes = (q1_dnode_t *) GetMemory(Q1_MAX_MAP_NODES * sizeof(q1_dnode_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_NODES * sizeof(q1_dnode_t);
 +	//texture info
 +	q1_numtexinfo = 0;
 +	q1_texinfo = (q1_texinfo_t *) GetMemory(Q1_MAX_MAP_TEXINFO * sizeof(q1_texinfo_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_TEXINFO * sizeof(q1_texinfo_t);
 +	//faces
 +	q1_numfaces = 0;
 +	q1_dfaces = (q1_dface_t *) GetMemory(Q1_MAX_MAP_FACES * sizeof(q1_dface_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_FACES * sizeof(q1_dface_t);
 +	//clip nodes
 +	q1_numclipnodes = 0;
 +	q1_dclipnodes = (q1_dclipnode_t *) GetMemory(Q1_MAX_MAP_CLIPNODES * sizeof(q1_dclipnode_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_CLIPNODES * sizeof(q1_dclipnode_t);
 +	//edges
 +	q1_numedges = 0;
 +	q1_dedges = (q1_dedge_t *) GetMemory(Q1_MAX_MAP_EDGES * sizeof(q1_dedge_t));
 +	q1_allocatedbspmem += Q1_MAX_MAP_EDGES, sizeof(q1_dedge_t);
 +	//mark surfaces
 +	q1_nummarksurfaces = 0;
 +	q1_dmarksurfaces = (unsigned short *) GetMemory(Q1_MAX_MAP_MARKSURFACES * sizeof(unsigned short));
 +	q1_allocatedbspmem += Q1_MAX_MAP_MARKSURFACES * sizeof(unsigned short);
 +	//surface edges
 +	q1_numsurfedges = 0;
 +	q1_dsurfedges = (int *) GetMemory(Q1_MAX_MAP_SURFEDGES * sizeof(int));
 +	q1_allocatedbspmem += Q1_MAX_MAP_SURFEDGES * sizeof(int);
 +	//print allocated memory
 +	Log_Print("allocated ");
 +	PrintMemorySize(q1_allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +} //end of the function Q1_AllocMaxBSP
 +
 +void Q1_FreeMaxBSP(void)
 +{
 +	//models
 +	q1_nummodels = 0;
 +	FreeMemory(q1_dmodels);
 +	q1_dmodels = NULL;
 +	//visibility
 +	q1_visdatasize = 0;
 +	FreeMemory(q1_dvisdata);
 +	q1_dvisdata = NULL;
 +	//light data
 +	q1_lightdatasize = 0;
 +	FreeMemory(q1_dlightdata);
 +	q1_dlightdata = NULL;
 +	//texture data
 +	q1_texdatasize = 0;
 +	FreeMemory(q1_dtexdata);
 +	q1_dtexdata = NULL;
 +	//entities
 +	q1_entdatasize = 0;
 +	FreeMemory(q1_dentdata);
 +	q1_dentdata = NULL;
 +	//leaves
 +	q1_numleafs = 0;
 +	FreeMemory(q1_dleafs);
 +	q1_dleafs = NULL;
 +	//planes
 +	q1_numplanes = 0;
 +	FreeMemory(q1_dplanes);
 +	q1_dplanes = NULL;
 +	//vertexes
 +	q1_numvertexes = 0;
 +	FreeMemory(q1_dvertexes);
 +	q1_dvertexes = NULL;
 +	//nodes
 +	q1_numnodes = 0;
 +	FreeMemory(q1_dnodes);
 +	q1_dnodes = NULL;
 +	//texture info
 +	q1_numtexinfo = 0;
 +	FreeMemory(q1_texinfo);
 +	q1_texinfo = NULL;
 +	//faces
 +	q1_numfaces = 0;
 +	FreeMemory(q1_dfaces);
 +	q1_dfaces = NULL;
 +	//clip nodes
 +	q1_numclipnodes = 0;
 +	FreeMemory(q1_dclipnodes);
 +	q1_dclipnodes = NULL;
 +	//edges
 +	q1_numedges = 0;
 +	FreeMemory(q1_dedges);
 +	q1_dedges = NULL;
 +	//mark surfaces
 +	q1_nummarksurfaces = 0;
 +	FreeMemory(q1_dmarksurfaces);
 +	q1_dmarksurfaces = NULL;
 +	//surface edges
 +	q1_numsurfedges = 0;
 +	FreeMemory(q1_dsurfedges);
 +	q1_dsurfedges = NULL;
 +	//
 +	Log_Print("freed ");
 +	PrintMemorySize(q1_allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +	q1_allocatedbspmem = 0;
 +} //end of the function Q1_FreeMaxBSP
 +//#endif //ME
 +
 +/*
 +=============
 +Q1_SwapBSPFile
 +
 +Byte swaps all data in a bsp file.
 +=============
 +*/
 +void Q1_SwapBSPFile (qboolean todisk)
 +{
 +	int i, j, c;
 +	q1_dmodel_t *d;
 +	q1_dmiptexlump_t *mtl;
 +
 +	
 +// models	
 +	for (i=0 ; i<q1_nummodels ; i++)
 +	{
 +		d = &q1_dmodels[i];
 +
 +		for (j=0 ; j<Q1_MAX_MAP_HULLS ; j++)
 +			d->headnode[j] = LittleLong (d->headnode[j]);
 +
 +		d->visleafs = LittleLong (d->visleafs);
 +		d->firstface = LittleLong (d->firstface);
 +		d->numfaces = LittleLong (d->numfaces);
 +		
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			d->mins[j] = LittleFloat(d->mins[j]);
 +			d->maxs[j] = LittleFloat(d->maxs[j]);
 +			d->origin[j] = LittleFloat(d->origin[j]);
 +		}
 +	}
 +
 +//
 +// vertexes
 +//
 +	for (i=0 ; i<q1_numvertexes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			q1_dvertexes[i].point[j] = LittleFloat(q1_dvertexes[i].point[j]);
 +	}
 +		
 +//
 +// planes
 +//	
 +	for (i=0 ; i<q1_numplanes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			q1_dplanes[i].normal[j] = LittleFloat(q1_dplanes[i].normal[j]);
 +		q1_dplanes[i].dist = LittleFloat(q1_dplanes[i].dist);
 +		q1_dplanes[i].type = LittleLong(q1_dplanes[i].type);
 +	}
 +	
 +//
 +// texinfos
 +//	
 +	for (i=0 ; i<q1_numtexinfo ; i++)
 +	{
 +		for (j=0 ; j<8 ; j++)
 +			q1_texinfo[i].vecs[0][j] = LittleFloat(q1_texinfo[i].vecs[0][j]);
 +		q1_texinfo[i].miptex = LittleLong(q1_texinfo[i].miptex);
 +		q1_texinfo[i].flags = LittleLong(q1_texinfo[i].flags);
 +	}
 +	
 +//
 +// faces
 +//
 +	for (i=0 ; i<q1_numfaces ; i++)
 +	{
 +		q1_dfaces[i].texinfo = LittleShort(q1_dfaces[i].texinfo);
 +		q1_dfaces[i].planenum = LittleShort(q1_dfaces[i].planenum);
 +		q1_dfaces[i].side = LittleShort(q1_dfaces[i].side);
 +		q1_dfaces[i].lightofs = LittleLong(q1_dfaces[i].lightofs);
 +		q1_dfaces[i].firstedge = LittleLong(q1_dfaces[i].firstedge);
 +		q1_dfaces[i].numedges = LittleShort(q1_dfaces[i].numedges);
 +	}
 +
 +//
 +// nodes
 +//
 +	for (i=0 ; i<q1_numnodes ; i++)
 +	{
 +		q1_dnodes[i].planenum = LittleLong(q1_dnodes[i].planenum);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			q1_dnodes[i].mins[j] = LittleShort(q1_dnodes[i].mins[j]);
 +			q1_dnodes[i].maxs[j] = LittleShort(q1_dnodes[i].maxs[j]);
 +		}
 +		q1_dnodes[i].children[0] = LittleShort(q1_dnodes[i].children[0]);
 +		q1_dnodes[i].children[1] = LittleShort(q1_dnodes[i].children[1]);
 +		q1_dnodes[i].firstface = LittleShort(q1_dnodes[i].firstface);
 +		q1_dnodes[i].numfaces = LittleShort(q1_dnodes[i].numfaces);
 +	}
 +
 +//
 +// leafs
 +//
 +	for (i=0 ; i<q1_numleafs ; i++)
 +	{
 +		q1_dleafs[i].contents = LittleLong(q1_dleafs[i].contents);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			q1_dleafs[i].mins[j] = LittleShort(q1_dleafs[i].mins[j]);
 +			q1_dleafs[i].maxs[j] = LittleShort(q1_dleafs[i].maxs[j]);
 +		}
 +
 +		q1_dleafs[i].firstmarksurface = LittleShort(q1_dleafs[i].firstmarksurface);
 +		q1_dleafs[i].nummarksurfaces = LittleShort(q1_dleafs[i].nummarksurfaces);
 +		q1_dleafs[i].visofs = LittleLong(q1_dleafs[i].visofs);
 +	}
 +
 +//
 +// clipnodes
 +//
 +	for (i=0 ; i<q1_numclipnodes ; i++)
 +	{
 +		q1_dclipnodes[i].planenum = LittleLong(q1_dclipnodes[i].planenum);
 +		q1_dclipnodes[i].children[0] = LittleShort(q1_dclipnodes[i].children[0]);
 +		q1_dclipnodes[i].children[1] = LittleShort(q1_dclipnodes[i].children[1]);
 +	}
 +
 +//
 +// miptex
 +//
 +	if (q1_texdatasize)
 +	{
 +		mtl = (q1_dmiptexlump_t *)q1_dtexdata;
 +		if (todisk)
 +			c = mtl->nummiptex;
 +		else
 +			c = LittleLong(mtl->nummiptex);
 +		mtl->nummiptex = LittleLong (mtl->nummiptex);
 +		for (i=0 ; i<c ; i++)
 +			mtl->dataofs[i] = LittleLong(mtl->dataofs[i]);
 +	}
 +	
 +//
 +// marksurfaces
 +//
 +	for (i=0 ; i<q1_nummarksurfaces ; i++)
 +		q1_dmarksurfaces[i] = LittleShort(q1_dmarksurfaces[i]);
 +
 +//
 +// surfedges
 +//
 +	for (i=0 ; i<q1_numsurfedges ; i++)
 +		q1_dsurfedges[i] = LittleLong(q1_dsurfedges[i]);
 +
 +//
 +// edges
 +//
 +	for (i=0 ; i<q1_numedges ; i++)
 +	{
 +		q1_dedges[i].v[0] = LittleShort(q1_dedges[i].v[0]);
 +		q1_dedges[i].v[1] = LittleShort(q1_dedges[i].v[1]);
 +	}
 +}
 +
 +
 +q1_dheader_t *q1_header;
 +int			q1_fileLength;
 +
 +int Q1_CopyLump (int lump, void *dest, int size, int maxsize)
 +{
 +	int		length, ofs;
 +
 +	length = q1_header->lumps[lump].filelen;
 +	ofs = q1_header->lumps[lump].fileofs;
 +	
 +	if (length % size) {
 +		Error ("LoadBSPFile: odd lump size");
 +	}
 +	// somehow things got out of range
 +	if ((length/size) > maxsize) {
 +		printf("WARNING: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize);
 +		length = maxsize * size;
 +	}
 +	if ( ofs + length > q1_fileLength ) {
 +		printf("WARNING: exceeded file length for lump %d\n", lump);
 +		length = q1_fileLength - ofs;
 +		if ( length <= 0 ) {
 +			return 0;
 +		}
 +	}
 +
 +	memcpy (dest, (byte *)q1_header + ofs, length);
 +
 +	return length / size;
 +}
 +
 +/*
 +=============
 +Q1_LoadBSPFile
 +=============
 +*/
 +void	Q1_LoadBSPFile(char *filename, int offset, int length)
 +{
 +	int			i;
 +	
 +//
 +// load the file header
 +//
 +	q1_fileLength = LoadFile(filename, (void **)&q1_header, offset, length);
 +
 +// swap the header
 +	for (i=0 ; i< sizeof(q1_dheader_t)/4 ; i++)
 +		((int *)q1_header)[i] = LittleLong ( ((int *)q1_header)[i]);
 +
 +	if (q1_header->version != Q1_BSPVERSION)
 +		Error ("%s is version %i, not %i", filename, i, Q1_BSPVERSION);
 +
 +	q1_nummodels = Q1_CopyLump (Q1_LUMP_MODELS, q1_dmodels, sizeof(q1_dmodel_t), Q1_MAX_MAP_MODELS );
 +	q1_numvertexes = Q1_CopyLump (Q1_LUMP_VERTEXES, q1_dvertexes, sizeof(q1_dvertex_t), Q1_MAX_MAP_VERTS );
 +	q1_numplanes = Q1_CopyLump (Q1_LUMP_PLANES, q1_dplanes, sizeof(q1_dplane_t), Q1_MAX_MAP_PLANES );
 +	q1_numleafs = Q1_CopyLump (Q1_LUMP_LEAFS, q1_dleafs, sizeof(q1_dleaf_t), Q1_MAX_MAP_LEAFS );
 +	q1_numnodes = Q1_CopyLump (Q1_LUMP_NODES, q1_dnodes, sizeof(q1_dnode_t), Q1_MAX_MAP_NODES );
 +	q1_numtexinfo = Q1_CopyLump (Q1_LUMP_TEXINFO, q1_texinfo, sizeof(q1_texinfo_t), Q1_MAX_MAP_TEXINFO );
 +	q1_numclipnodes = Q1_CopyLump (Q1_LUMP_CLIPNODES, q1_dclipnodes, sizeof(q1_dclipnode_t), Q1_MAX_MAP_CLIPNODES );
 +	q1_numfaces = Q1_CopyLump (Q1_LUMP_FACES, q1_dfaces, sizeof(q1_dface_t), Q1_MAX_MAP_FACES );
 +	q1_nummarksurfaces = Q1_CopyLump (Q1_LUMP_MARKSURFACES, q1_dmarksurfaces, sizeof(q1_dmarksurfaces[0]), Q1_MAX_MAP_MARKSURFACES );
 +	q1_numsurfedges = Q1_CopyLump (Q1_LUMP_SURFEDGES, q1_dsurfedges, sizeof(q1_dsurfedges[0]), Q1_MAX_MAP_SURFEDGES );
 +	q1_numedges = Q1_CopyLump (Q1_LUMP_EDGES, q1_dedges, sizeof(q1_dedge_t), Q1_MAX_MAP_EDGES );
 +
 +	q1_texdatasize = Q1_CopyLump (Q1_LUMP_TEXTURES, q1_dtexdata, 1, Q1_MAX_MAP_MIPTEX );
 +	q1_visdatasize = Q1_CopyLump (Q1_LUMP_VISIBILITY, q1_dvisdata, 1, Q1_MAX_MAP_VISIBILITY );
 +	q1_lightdatasize = Q1_CopyLump (Q1_LUMP_LIGHTING, q1_dlightdata, 1, Q1_MAX_MAP_LIGHTING );
 +	q1_entdatasize = Q1_CopyLump (Q1_LUMP_ENTITIES, q1_dentdata, 1, Q1_MAX_MAP_ENTSTRING );
 +
 +	FreeMemory(q1_header);		// everything has been copied out
 +		
 +//
 +// swap everything
 +//	
 +	Q1_SwapBSPFile (false);
 +}
 +
 +//============================================================================
 +
 +FILE *q1_wadfile;
 +q1_dheader_t q1_outheader;
 +
 +void Q1_AddLump (int lumpnum, void *data, int len)
 +{
 +	q1_lump_t *lump;
 +
 +	lump = &q1_header->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong(ftell(q1_wadfile));
 +	lump->filelen = LittleLong(len);
 +	SafeWrite(q1_wadfile, data, (len+3)&~3);
 +}
 +
 +/*
 +=============
 +Q1_WriteBSPFile
 +
 +Swaps the bsp file in place, so it should not be referenced again
 +=============
 +*/
 +void	Q1_WriteBSPFile (char *filename)
 +{		
 +	q1_header = &q1_outheader;
 +	memset (q1_header, 0, sizeof(q1_dheader_t));
 +	
 +	Q1_SwapBSPFile (true);
 +
 +	q1_header->version = LittleLong (Q1_BSPVERSION);
 +	
 +	q1_wadfile = SafeOpenWrite (filename);
 +	SafeWrite (q1_wadfile, q1_header, sizeof(q1_dheader_t));	// overwritten later
 +
 +	Q1_AddLump (Q1_LUMP_PLANES, q1_dplanes, q1_numplanes*sizeof(q1_dplane_t));
 +	Q1_AddLump (Q1_LUMP_LEAFS, q1_dleafs, q1_numleafs*sizeof(q1_dleaf_t));
 +	Q1_AddLump (Q1_LUMP_VERTEXES, q1_dvertexes, q1_numvertexes*sizeof(q1_dvertex_t));
 +	Q1_AddLump (Q1_LUMP_NODES, q1_dnodes, q1_numnodes*sizeof(q1_dnode_t));
 +	Q1_AddLump (Q1_LUMP_TEXINFO, q1_texinfo, q1_numtexinfo*sizeof(q1_texinfo_t));
 +	Q1_AddLump (Q1_LUMP_FACES, q1_dfaces, q1_numfaces*sizeof(q1_dface_t));
 +	Q1_AddLump (Q1_LUMP_CLIPNODES, q1_dclipnodes, q1_numclipnodes*sizeof(q1_dclipnode_t));
 +	Q1_AddLump (Q1_LUMP_MARKSURFACES, q1_dmarksurfaces, q1_nummarksurfaces*sizeof(q1_dmarksurfaces[0]));
 +	Q1_AddLump (Q1_LUMP_SURFEDGES, q1_dsurfedges, q1_numsurfedges*sizeof(q1_dsurfedges[0]));
 +	Q1_AddLump (Q1_LUMP_EDGES, q1_dedges, q1_numedges*sizeof(q1_dedge_t));
 +	Q1_AddLump (Q1_LUMP_MODELS, q1_dmodels, q1_nummodels*sizeof(q1_dmodel_t));
 +
 +	Q1_AddLump (Q1_LUMP_LIGHTING, q1_dlightdata, q1_lightdatasize);
 +	Q1_AddLump (Q1_LUMP_VISIBILITY, q1_dvisdata, q1_visdatasize);
 +	Q1_AddLump (Q1_LUMP_ENTITIES, q1_dentdata, q1_entdatasize);
 +	Q1_AddLump (Q1_LUMP_TEXTURES, q1_dtexdata, q1_texdatasize);
 +	
 +	fseek (q1_wadfile, 0, SEEK_SET);
 +	SafeWrite (q1_wadfile, q1_header, sizeof(q1_dheader_t));
 +	fclose (q1_wadfile);	
 +}
 +
 +//============================================================================
 +
 +/*
 +=============
 +Q1_PrintBSPFileSizes
 +
 +Dumps info about current file
 +=============
 +*/
 +void Q1_PrintBSPFileSizes (void)
 +{
 +	printf ("%5i planes       %6i\n"
 +		,q1_numplanes, (int)(q1_numplanes*sizeof(q1_dplane_t)));
 +	printf ("%5i vertexes     %6i\n"
 +		,q1_numvertexes, (int)(q1_numvertexes*sizeof(q1_dvertex_t)));
 +	printf ("%5i nodes        %6i\n"
 +		,q1_numnodes, (int)(q1_numnodes*sizeof(q1_dnode_t)));
 +	printf ("%5i texinfo      %6i\n"
 +		,q1_numtexinfo, (int)(q1_numtexinfo*sizeof(q1_texinfo_t)));
 +	printf ("%5i faces        %6i\n"
 +		,q1_numfaces, (int)(q1_numfaces*sizeof(q1_dface_t)));
 +	printf ("%5i clipnodes    %6i\n"
 +		,q1_numclipnodes, (int)(q1_numclipnodes*sizeof(q1_dclipnode_t)));
 +	printf ("%5i leafs        %6i\n"
 +		,q1_numleafs, (int)(q1_numleafs*sizeof(q1_dleaf_t)));
 +	printf ("%5i marksurfaces %6i\n"
 +		,q1_nummarksurfaces, (int)(q1_nummarksurfaces*sizeof(q1_dmarksurfaces[0])));
 +	printf ("%5i surfedges    %6i\n"
 +		,q1_numsurfedges, (int)(q1_numsurfedges*sizeof(q1_dmarksurfaces[0])));
 +	printf ("%5i edges        %6i\n"
 +		,q1_numedges, (int)(q1_numedges*sizeof(q1_dedge_t)));
 +	if (!q1_texdatasize)
 +		printf ("    0 textures          0\n");
 +	else
 +		printf ("%5i textures     %6i\n",((q1_dmiptexlump_t*)q1_dtexdata)->nummiptex, q1_texdatasize);
 +	printf ("      lightdata    %6i\n", q1_lightdatasize);
 +	printf ("      visdata      %6i\n", q1_visdatasize);
 +	printf ("      entdata      %6i\n", q1_entdatasize);
 +} //end of the function Q1_PrintBSPFileSizes
 +
 +
 +/*
 +================
 +Q1_ParseEntities
 +
 +Parses the dentdata string into entities
 +================
 +*/
 +void Q1_ParseEntities (void)
 +{
 +	script_t *script;
 +
 +	num_entities = 0;
 +	script = LoadScriptMemory(q1_dentdata, q1_entdatasize, "*Quake1 bsp file");
 +	SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES |
 +									SCFL_NOSTRINGESCAPECHARS);
 +
 +	while(ParseEntity(script))
 +	{
 +	} //end while
 +
 +	FreeScript(script);
 +} //end of the function Q1_ParseEntities
 +
 +
 +/*
 +================
 +Q1_UnparseEntities
 +
 +Generates the dentdata string from all the entities
 +================
 +*/
 +void Q1_UnparseEntities (void)
 +{
 +	char *buf, *end;
 +	epair_t *ep;
 +	char line[2048];
 +	int i;
 +	
 +	buf = q1_dentdata;
 +	end = buf;
 +	*end = 0;
 +	
 +	for (i=0 ; i<num_entities ; i++)
 +	{
 +		ep = entities[i].epairs;
 +		if (!ep)
 +			continue;	// ent got removed
 +		
 +		strcat (end,"{\n");
 +		end += 2;
 +				
 +		for (ep = entities[i].epairs ; ep ; ep=ep->next)
 +		{
 +			sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
 +			strcat (end, line);
 +			end += strlen(line);
 +		}
 +		strcat (end,"}\n");
 +		end += 2;
 +
 +		if (end > buf + Q1_MAX_MAP_ENTSTRING)
 +			Error ("Entity text too long");
 +	}
 +	q1_entdatasize = end - buf + 1;
 +} //end of the function Q1_UnparseEntities
 diff --git a/code/bspc/l_bsp_q1.h b/code/bspc/l_bsp_q1.h new file mode 100755 index 0000000..1c2cd92 --- /dev/null +++ b/code/bspc/l_bsp_q1.h @@ -0,0 +1,275 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +
 +// upper design bounds
 +
 +#define	Q1_MAX_MAP_HULLS		4
 +
 +#define	Q1_MAX_MAP_MODELS		256
 +#define	Q1_MAX_MAP_BRUSHES		4096
 +#define	Q1_MAX_MAP_ENTITIES	1024
 +#define	Q1_MAX_MAP_ENTSTRING	65536
 +
 +#define	Q1_MAX_MAP_PLANES		8192
 +#define	Q1_MAX_MAP_NODES		32767		// because negative shorts are contents
 +#define	Q1_MAX_MAP_CLIPNODES	32767		//
 +#define	Q1_MAX_MAP_LEAFS		32767		// 
 +#define	Q1_MAX_MAP_VERTS		65535
 +#define	Q1_MAX_MAP_FACES		65535
 +#define	Q1_MAX_MAP_MARKSURFACES 65535
 +#define	Q1_MAX_MAP_TEXINFO		4096
 +#define	Q1_MAX_MAP_EDGES		256000
 +#define	Q1_MAX_MAP_SURFEDGES	512000
 +#define	Q1_MAX_MAP_MIPTEX		0x200000
 +#define	Q1_MAX_MAP_LIGHTING	0x100000
 +#define	Q1_MAX_MAP_VISIBILITY	0x100000
 +
 +// key / value pair sizes
 +
 +#define	MAX_KEY		32
 +#define	MAX_VALUE	1024
 +
 +//=============================================================================
 +
 +
 +#define Q1_BSPVERSION	29
 +
 +typedef struct
 +{
 +	int fileofs, filelen;
 +} q1_lump_t;
 +
 +#define	Q1_LUMP_ENTITIES	0
 +#define	Q1_LUMP_PLANES		1
 +#define	Q1_LUMP_TEXTURES	2
 +#define	Q1_LUMP_VERTEXES	3
 +#define	Q1_LUMP_VISIBILITY	4
 +#define	Q1_LUMP_NODES		5
 +#define	Q1_LUMP_TEXINFO	6
 +#define	Q1_LUMP_FACES		7
 +#define	Q1_LUMP_LIGHTING	8
 +#define	Q1_LUMP_CLIPNODES	9
 +#define	Q1_LUMP_LEAFS		10
 +#define	Q1_LUMP_MARKSURFACES 11
 +#define	Q1_LUMP_EDGES		12
 +#define	Q1_LUMP_SURFEDGES	13
 +#define	Q1_LUMP_MODELS		14
 +
 +#define	Q1_HEADER_LUMPS	15
 +
 +typedef struct
 +{
 +	float		mins[3], maxs[3];
 +	float		origin[3];
 +	int		headnode[Q1_MAX_MAP_HULLS];
 +	int		visleafs;		// not including the solid leaf 0
 +	int		firstface, numfaces;
 +} q1_dmodel_t;
 +
 +typedef struct
 +{
 +	int			version;	
 +	q1_lump_t	lumps[Q1_HEADER_LUMPS];
 +} q1_dheader_t;
 +
 +typedef struct
 +{
 +	int		nummiptex;
 +	int		dataofs[4];		// [nummiptex]
 +} q1_dmiptexlump_t;
 +
 +#define	MIPLEVELS	4
 +typedef struct q1_miptex_s
 +{
 +	char		name[16];
 +	unsigned	width, height;
 +	unsigned	offsets[MIPLEVELS];		// four mip maps stored
 +} q1_miptex_t;
 +
 +
 +typedef struct
 +{
 +	float	point[3];
 +} q1_dvertex_t;
 +
 +
 +// 0-2 are axial planes
 +#define	PLANE_X			0
 +#define	PLANE_Y			1
 +#define	PLANE_Z			2
 +
 +// 3-5 are non-axial planes snapped to the nearest
 +#define	PLANE_ANYX		3
 +#define	PLANE_ANYY		4
 +#define	PLANE_ANYZ		5
 +
 +typedef struct
 +{
 +	float	normal[3];
 +	float	dist;
 +	int		type;		// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
 +} q1_dplane_t;
 +
 +
 +
 +#define	Q1_CONTENTS_EMPTY		-1
 +#define	Q1_CONTENTS_SOLID		-2
 +#define	Q1_CONTENTS_WATER		-3
 +#define	Q1_CONTENTS_SLIME		-4
 +#define	Q1_CONTENTS_LAVA		-5
 +#define	Q1_CONTENTS_SKY		-6
 +
 +// !!! if this is changed, it must be changed in asm_i386.h too !!!
 +typedef struct
 +{
 +	int		planenum;
 +	short		children[2];	// negative numbers are -(leafs+1), not nodes
 +	short		mins[3];		// for sphere culling
 +	short		maxs[3];
 +	unsigned short	firstface;
 +	unsigned short	numfaces;	// counting both sides
 +} q1_dnode_t;
 +
 +typedef struct
 +{
 +	int		planenum;
 +	short		children[2];	// negative numbers are contents
 +} q1_dclipnode_t;
 +
 +
 +typedef struct q1_texinfo_s
 +{
 +	float		vecs[2][4];		// [s/t][xyz offset]
 +	int		miptex;
 +	int		flags;
 +} q1_texinfo_t;
 +#define	TEX_SPECIAL		1		// sky or slime, no lightmap or 256 subdivision
 +
 +// note that edge 0 is never used, because negative edge nums are used for
 +// counterclockwise use of the edge in a face
 +typedef struct
 +{
 +	unsigned short	v[2];		// vertex numbers
 +} q1_dedge_t;
 +
 +#define	MAXLIGHTMAPS	4
 +typedef struct
 +{
 +	short		planenum;
 +	short		side;
 +
 +	int		firstedge;		// we must support > 64k edges
 +	short		numedges;	
 +	short		texinfo;
 +
 +// lighting info
 +	byte		styles[MAXLIGHTMAPS];
 +	int		lightofs;		// start of [numstyles*surfsize] samples
 +} q1_dface_t;
 +
 +
 +
 +#define	AMBIENT_WATER	0
 +#define	AMBIENT_SKY		1
 +#define	AMBIENT_SLIME	2
 +#define	AMBIENT_LAVA	3
 +
 +#define	NUM_AMBIENTS			4		// automatic ambient sounds
 +
 +// leaf 0 is the generic Q1_CONTENTS_SOLID leaf, used for all solid areas
 +// all other leafs need visibility info
 +typedef struct
 +{
 +	int				contents;
 +	int				visofs;				// -1 = no visibility info
 +
 +	short				mins[3];			// for frustum culling
 +	short				maxs[3];
 +
 +	unsigned short	firstmarksurface;
 +	unsigned short	nummarksurfaces;
 +
 +	byte		ambient_level[NUM_AMBIENTS];
 +} q1_dleaf_t;
 +
 +//============================================================================
 +
 +#ifndef QUAKE_GAME
 +
 +// the utilities get to be lazy and just use large static arrays
 +
 +extern	int				q1_nummodels;
 +extern	q1_dmodel_t		*q1_dmodels;//[MAX_MAP_MODELS];
 +
 +extern	int				q1_visdatasize;
 +extern	byte				*q1_dvisdata;//[MAX_MAP_VISIBILITY];
 +
 +extern	int				q1_lightdatasize;
 +extern	byte				*q1_dlightdata;//[MAX_MAP_LIGHTING];
 +
 +extern	int				q1_texdatasize;
 +extern	byte				*q1_dtexdata;//[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
 +
 +extern	int				q1_entdatasize;
 +extern	char				*q1_dentdata;//[MAX_MAP_ENTSTRING];
 +
 +extern	int				q1_numleafs;
 +extern	q1_dleaf_t		*q1_dleafs;//[MAX_MAP_LEAFS];
 +
 +extern	int				q1_numplanes;
 +extern	q1_dplane_t		*q1_dplanes;//[MAX_MAP_PLANES];
 +
 +extern	int				q1_numvertexes;
 +extern	q1_dvertex_t	*q1_dvertexes;//[MAX_MAP_VERTS];
 +
 +extern	int				q1_numnodes;
 +extern	q1_dnode_t		*q1_dnodes;//[MAX_MAP_NODES];
 +
 +extern	int				q1_numtexinfo;
 +extern	q1_texinfo_t	*q1_texinfo;//[MAX_MAP_TEXINFO];
 +
 +extern	int				q1_numfaces;
 +extern	q1_dface_t		*q1_dfaces;//[MAX_MAP_FACES];
 +
 +extern	int				q1_numclipnodes;
 +extern	q1_dclipnode_t	*q1_dclipnodes;//[MAX_MAP_CLIPNODES];
 +
 +extern	int				q1_numedges;
 +extern	q1_dedge_t		*q1_dedges;//[MAX_MAP_EDGES];
 +
 +extern	int				q1_nummarksurfaces;
 +extern	unsigned short	*q1_dmarksurfaces;//[MAX_MAP_MARKSURFACES];
 +
 +extern	int				q1_numsurfedges;
 +extern	int				*q1_dsurfedges;//[MAX_MAP_SURFEDGES];
 +
 +
 +void Q1_AllocMaxBSP(void);
 +void Q1_FreeMaxBSP(void);
 +void Q1_LoadBSPFile(char *filename, int offset, int length);
 +void Q1_WriteBSPFile(char *filename);
 +void Q1_PrintBSPFileSizes(void);
 +void Q1_ParseEntities(void);
 +void Q1_UnparseEntities(void);
 +
 +#endif
 diff --git a/code/bspc/l_bsp_q2.c b/code/bspc/l_bsp_q2.c new file mode 100755 index 0000000..d5e9844 --- /dev/null +++ b/code/bspc/l_bsp_q2.c @@ -0,0 +1,1134 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_mem.h"
 +#include "l_log.h"
 +#include "l_poly.h"
 +#include "../botlib/l_script.h"
 +#include "q2files.h"
 +#include "l_bsp_q2.h"
 +#include "l_bsp_ent.h"
 +
 +#define q2_dmodel_t			dmodel_t
 +#define q2_lump_t				lump_t
 +#define q2_dheader_t			dheader_t
 +#define q2_dmodel_t			dmodel_t
 +#define q2_dvertex_t			dvertex_t
 +#define q2_dplane_t			dplane_t
 +#define q2_dnode_t			dnode_t
 +#define q2_texinfo_t			texinfo_t
 +#define q2_dedge_t			dedge_t
 +#define q2_dface_t			dface_t
 +#define q2_dleaf_t			dleaf_t
 +#define q2_dbrushside_t		dbrushside_t
 +#define q2_dbrush_t			dbrush_t
 +#define q2_dvis_t				dvis_t
 +#define q2_dareaportal_t	dareaportal_t
 +#define q2_darea_t			darea_t
 +
 +#define q2_nummodels			nummodels
 +#define q2_dmodels			dmodels
 +#define q2_numleafs			numleafs
 +#define q2_dleafs				dleafs
 +#define q2_numplanes			numplanes
 +#define q2_dplanes			dplanes
 +#define q2_numvertexes		numvertexes
 +#define q2_dvertexes			dvertexes
 +#define q2_numnodes			numnodes
 +#define q2_dnodes				dnodes
 +#define q2_numtexinfo		numtexinfo
 +#define q2_texinfo			texinfo
 +#define q2_numfaces			numfaces
 +#define q2_dfaces				dfaces
 +#define q2_numedges			numedges
 +#define q2_dedges				dedges
 +#define q2_numleaffaces		numleaffaces
 +#define q2_dleaffaces		dleaffaces
 +#define q2_numleafbrushes	numleafbrushes
 +#define q2_dleafbrushes		dleafbrushes
 +#define q2_dsurfedges		dsurfedges
 +#define q2_numbrushes		numbrushes
 +#define q2_dbrushes			dbrushes
 +#define q2_numbrushsides	numbrushsides
 +#define q2_dbrushsides		dbrushsides
 +#define q2_numareas			numareas
 +#define q2_dareas				dareas
 +#define q2_numareaportals	numareaportals
 +#define q2_dareaportals		dareaportals
 +
 +void GetLeafNums (void);
 +
 +//=============================================================================
 +
 +int				nummodels;
 +dmodel_t			*dmodels;//[MAX_MAP_MODELS];
 +
 +int				visdatasize;
 +byte				*dvisdata;//[MAX_MAP_VISIBILITY];
 +dvis_t			*dvis;// = (dvis_t *)dvisdata;
 +
 +int				lightdatasize;
 +byte				*dlightdata;//[MAX_MAP_LIGHTING];
 +
 +int				entdatasize;
 +char				*dentdata;//[MAX_MAP_ENTSTRING];
 +
 +int				numleafs;
 +dleaf_t			*dleafs;//[MAX_MAP_LEAFS];
 +
 +int				numplanes;
 +dplane_t			*dplanes;//[MAX_MAP_PLANES];
 +
 +int				numvertexes;
 +dvertex_t		*dvertexes;//[MAX_MAP_VERTS];
 +
 +int				numnodes;
 +dnode_t			*dnodes;//[MAX_MAP_NODES];
 +
 +//NOTE: must be static for q2 .map to q2 .bsp
 +int				numtexinfo;
 +texinfo_t		texinfo[MAX_MAP_TEXINFO];
 +
 +int				numfaces;
 +dface_t			*dfaces;//[MAX_MAP_FACES];
 +
 +int				numedges;
 +dedge_t			*dedges;//[MAX_MAP_EDGES];
 +
 +int				numleaffaces;
 +unsigned short	*dleaffaces;//[MAX_MAP_LEAFFACES];
 +
 +int				numleafbrushes;
 +unsigned short	*dleafbrushes;//[MAX_MAP_LEAFBRUSHES];
 +
 +int				numsurfedges;
 +int				*dsurfedges;//[MAX_MAP_SURFEDGES];
 +
 +int				numbrushes;
 +dbrush_t			*dbrushes;//[MAX_MAP_BRUSHES];
 +
 +int				numbrushsides;
 +dbrushside_t	*dbrushsides;//[MAX_MAP_BRUSHSIDES];
 +
 +int				numareas;
 +darea_t			*dareas;//[MAX_MAP_AREAS];
 +
 +int				numareaportals;
 +dareaportal_t	*dareaportals;//[MAX_MAP_AREAPORTALS];
 +
 +#define MAX_MAP_DPOP			256
 +byte				dpop[MAX_MAP_DPOP];
 +
 +//
 +char brushsidetextured[MAX_MAP_BRUSHSIDES];
 +
 +//#ifdef ME
 +
 +int bspallocated = false;
 +int allocatedbspmem = 0;
 +
 +void Q2_AllocMaxBSP(void)
 +{
 +	//models
 +	nummodels = 0;
 +	dmodels = (dmodel_t *) GetClearedMemory(MAX_MAP_MODELS * sizeof(dmodel_t));
 +	allocatedbspmem += MAX_MAP_MODELS * sizeof(dmodel_t);
 +	//vis data
 +	visdatasize = 0;
 +	dvisdata = (byte *) GetClearedMemory(MAX_MAP_VISIBILITY * sizeof(byte));
 +	dvis = (dvis_t *) dvisdata;
 +	allocatedbspmem += MAX_MAP_VISIBILITY * sizeof(byte);
 +	//light data
 +	lightdatasize = 0;
 +	dlightdata = (byte *) GetClearedMemory(MAX_MAP_LIGHTING * sizeof(byte));
 +	allocatedbspmem += MAX_MAP_LIGHTING * sizeof(byte);
 +	//entity data
 +	entdatasize = 0;
 +	dentdata = (char *) GetClearedMemory(MAX_MAP_ENTSTRING * sizeof(char));
 +	allocatedbspmem += MAX_MAP_ENTSTRING * sizeof(char);
 +	//leafs
 +	numleafs = 0;
 +	dleafs = (dleaf_t *) GetClearedMemory(MAX_MAP_LEAFS * sizeof(dleaf_t));
 +	allocatedbspmem += MAX_MAP_LEAFS * sizeof(dleaf_t);
 +	//planes
 +	numplanes = 0;
 +	dplanes = (dplane_t *) GetClearedMemory(MAX_MAP_PLANES * sizeof(dplane_t));
 +	allocatedbspmem += MAX_MAP_PLANES * sizeof(dplane_t);
 +	//vertexes
 +	numvertexes = 0;
 +	dvertexes = (dvertex_t *) GetClearedMemory(MAX_MAP_VERTS * sizeof(dvertex_t));
 +	allocatedbspmem += MAX_MAP_VERTS * sizeof(dvertex_t);
 +	//nodes
 +	numnodes = 0;
 +	dnodes = (dnode_t *) GetClearedMemory(MAX_MAP_NODES * sizeof(dnode_t));
 +	allocatedbspmem += MAX_MAP_NODES * sizeof(dnode_t);
 +	/*
 +	//texture info
 +	numtexinfo = 0;
 +	texinfo = (texinfo_t *) GetClearedMemory(MAX_MAP_TEXINFO * sizeof(texinfo_t));
 +	allocatedbspmem += MAX_MAP_TEXINFO * sizeof(texinfo_t);
 +	//*/
 +	//faces
 +	numfaces = 0;
 +	dfaces = (dface_t *) GetClearedMemory(MAX_MAP_FACES * sizeof(dface_t));
 +	allocatedbspmem += MAX_MAP_FACES * sizeof(dface_t);
 +	//edges
 +	numedges = 0;
 +	dedges = (dedge_t *) GetClearedMemory(MAX_MAP_EDGES * sizeof(dedge_t));
 +	allocatedbspmem += MAX_MAP_EDGES * sizeof(dedge_t);
 +	//leaf faces
 +	numleaffaces = 0;
 +	dleaffaces = (unsigned short *) GetClearedMemory(MAX_MAP_LEAFFACES * sizeof(unsigned short));
 +	allocatedbspmem += MAX_MAP_LEAFFACES * sizeof(unsigned short);
 +	//leaf brushes
 +	numleafbrushes = 0;
 +	dleafbrushes = (unsigned short *) GetClearedMemory(MAX_MAP_LEAFBRUSHES * sizeof(unsigned short));
 +	allocatedbspmem += MAX_MAP_LEAFBRUSHES * sizeof(unsigned short);
 +	//surface edges
 +	numsurfedges = 0;
 +	dsurfedges = (int *) GetClearedMemory(MAX_MAP_SURFEDGES * sizeof(int));
 +	allocatedbspmem += MAX_MAP_SURFEDGES * sizeof(int);
 +	//brushes
 +	numbrushes = 0;
 +	dbrushes = (dbrush_t *) GetClearedMemory(MAX_MAP_BRUSHES * sizeof(dbrush_t));
 +	allocatedbspmem += MAX_MAP_BRUSHES * sizeof(dbrush_t);
 +	//brushsides
 +	numbrushsides = 0;
 +	dbrushsides = (dbrushside_t *) GetClearedMemory(MAX_MAP_BRUSHSIDES * sizeof(dbrushside_t));
 +	allocatedbspmem += MAX_MAP_BRUSHSIDES * sizeof(dbrushside_t);
 +	//areas
 +	numareas = 0;
 +	dareas = (darea_t *) GetClearedMemory(MAX_MAP_AREAS * sizeof(darea_t));
 +	allocatedbspmem += MAX_MAP_AREAS * sizeof(darea_t);
 +	//area portals
 +	numareaportals = 0;
 +	dareaportals = (dareaportal_t *) GetClearedMemory(MAX_MAP_AREAPORTALS * sizeof(dareaportal_t));
 +	allocatedbspmem += MAX_MAP_AREAPORTALS * sizeof(dareaportal_t);
 +	//print allocated memory
 +	Log_Print("allocated ");
 +	PrintMemorySize(allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +} //end of the function Q2_AllocMaxBSP
 +
 +void Q2_FreeMaxBSP(void)
 +{
 +	//models
 +	nummodels = 0;
 +	FreeMemory(dmodels);
 +	dmodels = NULL;
 +	//vis data
 +	visdatasize = 0;
 +	FreeMemory(dvisdata);
 +	dvisdata = NULL;
 +	dvis = NULL;
 +	//light data
 +	lightdatasize = 0;
 +	FreeMemory(dlightdata);
 +	dlightdata = NULL;
 +	//entity data
 +	entdatasize = 0;
 +	FreeMemory(dentdata);
 +	dentdata = NULL;
 +	//leafs
 +	numleafs = 0;
 +	FreeMemory(dleafs);
 +	dleafs = NULL;
 +	//planes
 +	numplanes = 0;
 +	FreeMemory(dplanes);
 +	dplanes = NULL;
 +	//vertexes
 +	numvertexes = 0;
 +	FreeMemory(dvertexes);
 +	dvertexes = NULL;
 +	//nodes
 +	numnodes = 0;
 +	FreeMemory(dnodes);
 +	dnodes = NULL;
 +	/*
 +	//texture info
 +	numtexinfo = 0;
 +	FreeMemory(texinfo);
 +	texinfo = NULL;
 +	//*/
 +	//faces
 +	numfaces = 0;
 +	FreeMemory(dfaces);
 +	dfaces = NULL;
 +	//edges
 +	numedges = 0;
 +	FreeMemory(dedges);
 +	dedges = NULL;
 +	//leaf faces
 +	numleaffaces = 0;
 +	FreeMemory(dleaffaces);
 +	dleaffaces = NULL;
 +	//leaf brushes
 +	numleafbrushes = 0;
 +	FreeMemory(dleafbrushes);
 +	dleafbrushes = NULL;
 +	//surface edges
 +	numsurfedges = 0;
 +	FreeMemory(dsurfedges);
 +	dsurfedges = NULL;
 +	//brushes
 +	numbrushes = 0;
 +	FreeMemory(dbrushes);
 +	dbrushes = NULL;
 +	//brushsides
 +	numbrushsides = 0;
 +	FreeMemory(dbrushsides);
 +	dbrushsides = NULL;
 +	//areas
 +	numareas = 0;
 +	FreeMemory(dareas);
 +	dareas = NULL;
 +	//area portals
 +	numareaportals = 0;
 +	FreeMemory(dareaportals);
 +	dareaportals = NULL;
 +	//
 +	Log_Print("freed ");
 +	PrintMemorySize(allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +	allocatedbspmem = 0;
 +} //end of the function Q2_FreeMaxBSP
 +
 +#define WCONVEX_EPSILON		0.5
 +
 +int InsideWinding(winding_t *w, vec3_t point, int planenum)
 +{
 +	int i;
 +	float dist;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	dplane_t *plane;
 +
 +	for (i = 1; i <= w->numpoints; i++)
 +	{
 +		v1 = w->p[i % w->numpoints];
 +		v2 = w->p[(i + 1) % w->numpoints];
 +
 +		VectorSubtract(v2, v1, edgevec);
 +		plane = &dplanes[planenum];
 +		CrossProduct(plane->normal, edgevec, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		if (DotProduct(normal, point) - dist > WCONVEX_EPSILON) return false;
 +	} //end for
 +	return true;
 +} //end of the function InsideWinding
 +
 +int InsideFace(dface_t *face, vec3_t point)
 +{
 +	int i, edgenum, side;
 +	float dist;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	dplane_t *plane;
 +
 +	for (i = 0; i < face->numedges; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = dsurfedges[face->firstedge + i];
 +		side = edgenum < 0;
 +		v1 = dvertexes[dedges[abs(edgenum)].v[side]].point;
 +		v2 = dvertexes[dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing out of the face
 +		VectorSubtract(v1, v2, edgevec);
 +		plane = &dplanes[face->planenum];
 +		CrossProduct(plane->normal, edgevec, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		if (DotProduct(normal, point) - dist > WCONVEX_EPSILON) return false;
 +	} //end for
 +	return true;
 +} //end of the function InsideFace
 +//===========================================================================
 +// returns the amount the face and the winding overlap
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +float Q2_FaceOnWinding(q2_dface_t *face, winding_t *winding)
 +{
 +	int i, edgenum, side;
 +	float dist, area;
 +	q2_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	winding_t *w;
 +
 +	//
 +	w = CopyWinding(winding);
 +	memcpy(&plane, &q2_dplanes[face->planenum], sizeof(q2_dplane_t));
 +	//check on which side of the plane the face is
 +	if (face->side)
 +	{
 +		VectorNegate(plane.normal, plane.normal);
 +		plane.dist = -plane.dist;
 +	} //end if
 +	for (i = 0; i < face->numedges && w; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = q2_dsurfedges[face->firstedge + i];
 +		side = edgenum > 0;
 +		//if the face plane is flipped
 +		v1 = q2_dvertexes[q2_dedges[abs(edgenum)].v[side]].point;
 +		v2 = q2_dvertexes[q2_dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing inward
 +		VectorSubtract(v1, v2, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		ChopWindingInPlace(&w, normal, dist, -0.1); //CLIP_EPSILON
 +	} //end for
 +	if (w)
 +	{
 +		area = WindingArea(w);
 +		FreeWinding(w);
 +		return area;
 +	} //end if
 +	return 0;
 +} //end of the function Q2_FaceOnWinding
 +//===========================================================================
 +// creates a winding for the given brush side on the given brush
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +winding_t *Q2_BrushSideWinding(dbrush_t *brush, dbrushside_t *baseside)
 +{
 +	int i;
 +	dplane_t *baseplane, *plane;
 +	winding_t *w;
 +	dbrushside_t *side;
 +	
 +	//create a winding for the brush side with the given planenumber
 +	baseplane = &dplanes[baseside->planenum];
 +	w = BaseWindingForPlane(baseplane->normal, baseplane->dist);
 +	for (i = 0; i < brush->numsides && w; i++)
 +	{
 +		side = &dbrushsides[brush->firstside + i];
 +		//don't chop with the base plane
 +		if (side->planenum == baseside->planenum) continue;
 +		//also don't use planes that are almost equal
 +		plane = &dplanes[side->planenum];
 +		if (DotProduct(baseplane->normal, plane->normal) > 0.999
 +				&& fabs(baseplane->dist - plane->dist) < 0.01) continue;
 +		//
 +		plane = &dplanes[side->planenum^1];
 +		ChopWindingInPlace(&w, plane->normal, plane->dist, -0.1); //CLIP_EPSILON);
 +	} //end for
 +	return w;
 +} //end of the function Q2_BrushSideWinding
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Q2_HintSkipBrush(dbrush_t *brush)
 +{
 +	int j;
 +	dbrushside_t *brushside;
 +
 +	for (j = 0; j < brush->numsides; j++)
 +	{
 +		brushside = &dbrushsides[brush->firstside + j];
 +		if (brushside->texinfo > 0)
 +		{
 +			if (texinfo[brushside->texinfo].flags & (SURF_SKIP|SURF_HINT))
 +			{
 +				return true;
 +			} //end if
 +		} //end if
 +	} //end for
 +	return false;
 +} //end of the function Q2_HintSkipBrush
 +//===========================================================================
 +// fix screwed brush texture references
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WindingIsTiny(winding_t *w);
 +
 +void Q2_FixTextureReferences(void)
 +{
 +	int i, j, k, we;
 +	dbrushside_t *brushside;
 +	dbrush_t *brush;
 +	dface_t *face;
 +	winding_t *w;
 +
 +	memset(brushsidetextured, false, MAX_MAP_BRUSHSIDES);
 +	//go over all the brushes
 +   for (i = 0; i < numbrushes; i++)
 +   {
 +		brush = &dbrushes[i];
 +		//hint brushes are not textured
 +		if (Q2_HintSkipBrush(brush)) continue;
 +		//go over all the sides of the brush
 +		for (j = 0; j < brush->numsides; j++)
 +		{
 +			brushside = &dbrushsides[brush->firstside + j];
 +			//
 +			w = Q2_BrushSideWinding(brush, brushside);
 +			if (!w)
 +			{
 +				brushsidetextured[brush->firstside + j] = true;
 +				continue;
 +			} //end if
 +			else
 +			{
 +				//RemoveEqualPoints(w, 0.2);
 +				if (WindingIsTiny(w))
 +				{
 +					FreeWinding(w);
 +					brushsidetextured[brush->firstside + j] = true;
 +					continue;
 +				} //end if
 +				else
 +				{
 +					we = WindingError(w);
 +					if (we == WE_NOTENOUGHPOINTS
 +						|| we == WE_SMALLAREA
 +						|| we == WE_POINTBOGUSRANGE
 +//						|| we == WE_NONCONVEX
 +						)
 +					{
 +						FreeWinding(w);
 +						brushsidetextured[brush->firstside + j] = true;
 +						continue;
 +					} //end if
 +				} //end else
 +			} //end else
 +			if (WindingArea(w) < 20)
 +			{
 +				brushsidetextured[brush->firstside + j] = true;
 +			} //end if
 +			//find a face for texturing this brush
 +			for (k = 0; k < numfaces; k++)
 +			{
 +				face = &dfaces[k];
 +				//if the face is in the same plane as the brush side
 +				if ((face->planenum&~1) != (brushside->planenum&~1)) continue;
 +				//if the face is partly or totally on the brush side
 +				if (Q2_FaceOnWinding(face, w))
 +				{
 +					brushside->texinfo = face->texinfo;
 +					brushsidetextured[brush->firstside + j] = true;
 +					break;
 +				} //end if
 +			} //end for
 +			FreeWinding(w);
 +		} //end for
 +	} //end for
 +} //end of the function Q2_FixTextureReferences*/
 +
 +//#endif //ME
 +
 +
 +/*
 +===============
 +CompressVis
 +
 +===============
 +*/
 +int Q2_CompressVis (byte *vis, byte *dest)
 +{
 +	int		j;
 +	int		rep;
 +	int		visrow;
 +	byte	*dest_p;
 +	
 +	dest_p = dest;
 +//	visrow = (r_numvisleafs + 7)>>3;
 +	visrow = (dvis->numclusters + 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;
 +}
 +
 +
 +/*
 +===================
 +DecompressVis
 +===================
 +*/
 +void Q2_DecompressVis (byte *in, byte *decompressed)
 +{
 +	int		c;
 +	byte	*out;
 +	int		row;
 +
 +//	row = (r_numvisleafs+7)>>3;	
 +	row = (dvis->numclusters+7)>>3;	
 +	out = decompressed;
 +
 +	do
 +	{
 +		if (*in)
 +		{
 +			*out++ = *in++;
 +			continue;
 +		}
 +	
 +		c = in[1];
 +		if (!c)
 +			Error ("DecompressVis: 0 repeat");
 +		in += 2;
 +		while (c)
 +		{
 +			*out++ = 0;
 +			c--;
 +		}
 +	} while (out - decompressed < row);
 +}
 +
 +//=============================================================================
 +
 +/*
 +=============
 +SwapBSPFile
 +
 +Byte swaps all data in a bsp file.
 +=============
 +*/
 +void Q2_SwapBSPFile (qboolean todisk)
 +{
 +	int				i, j;
 +	dmodel_t		*d;
 +
 +	
 +// models	
 +	for (i=0 ; i<nummodels ; i++)
 +	{
 +		d = &dmodels[i];
 +
 +		d->firstface = LittleLong (d->firstface);
 +		d->numfaces = LittleLong (d->numfaces);
 +		d->headnode = LittleLong (d->headnode);
 +		
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			d->mins[j] = LittleFloat(d->mins[j]);
 +			d->maxs[j] = LittleFloat(d->maxs[j]);
 +			d->origin[j] = LittleFloat(d->origin[j]);
 +		}
 +	}
 +
 +//
 +// vertexes
 +//
 +	for (i=0 ; i<numvertexes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			dvertexes[i].point[j] = LittleFloat (dvertexes[i].point[j]);
 +	}
 +		
 +//
 +// planes
 +//	
 +	for (i=0 ; i<numplanes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			dplanes[i].normal[j] = LittleFloat (dplanes[i].normal[j]);
 +		dplanes[i].dist = LittleFloat (dplanes[i].dist);
 +		dplanes[i].type = LittleLong (dplanes[i].type);
 +	}
 +	
 +//
 +// texinfos
 +//	
 +	for (i=0 ; i<numtexinfo ; i++)
 +	{
 +		for (j=0 ; j<8 ; j++)
 +			texinfo[i].vecs[0][j] = LittleFloat (texinfo[i].vecs[0][j]);
 +		texinfo[i].flags = LittleLong (texinfo[i].flags);
 +		texinfo[i].value = LittleLong (texinfo[i].value);
 +		texinfo[i].nexttexinfo = LittleLong (texinfo[i].nexttexinfo);
 +	}
 +	
 +//
 +// faces
 +//
 +	for (i=0 ; i<numfaces ; i++)
 +	{
 +		dfaces[i].texinfo = LittleShort (dfaces[i].texinfo);
 +		dfaces[i].planenum = LittleShort (dfaces[i].planenum);
 +		dfaces[i].side = LittleShort (dfaces[i].side);
 +		dfaces[i].lightofs = LittleLong (dfaces[i].lightofs);
 +		dfaces[i].firstedge = LittleLong (dfaces[i].firstedge);
 +		dfaces[i].numedges = LittleShort (dfaces[i].numedges);
 +	}
 +
 +//
 +// nodes
 +//
 +	for (i=0 ; i<numnodes ; i++)
 +	{
 +		dnodes[i].planenum = LittleLong (dnodes[i].planenum);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			dnodes[i].mins[j] = LittleShort (dnodes[i].mins[j]);
 +			dnodes[i].maxs[j] = LittleShort (dnodes[i].maxs[j]);
 +		}
 +		dnodes[i].children[0] = LittleLong (dnodes[i].children[0]);
 +		dnodes[i].children[1] = LittleLong (dnodes[i].children[1]);
 +		dnodes[i].firstface = LittleShort (dnodes[i].firstface);
 +		dnodes[i].numfaces = LittleShort (dnodes[i].numfaces);
 +	}
 +
 +//
 +// leafs
 +//
 +	for (i=0 ; i<numleafs ; i++)
 +	{
 +		dleafs[i].contents = LittleLong (dleafs[i].contents);
 +		dleafs[i].cluster = LittleShort (dleafs[i].cluster);
 +		dleafs[i].area = LittleShort (dleafs[i].area);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			dleafs[i].mins[j] = LittleShort (dleafs[i].mins[j]);
 +			dleafs[i].maxs[j] = LittleShort (dleafs[i].maxs[j]);
 +		}
 +
 +		dleafs[i].firstleafface = LittleShort (dleafs[i].firstleafface);
 +		dleafs[i].numleaffaces = LittleShort (dleafs[i].numleaffaces);
 +		dleafs[i].firstleafbrush = LittleShort (dleafs[i].firstleafbrush);
 +		dleafs[i].numleafbrushes = LittleShort (dleafs[i].numleafbrushes);
 +	}
 +
 +//
 +// leaffaces
 +//
 +	for (i=0 ; i<numleaffaces ; i++)
 +		dleaffaces[i] = LittleShort (dleaffaces[i]);
 +
 +//
 +// leafbrushes
 +//
 +	for (i=0 ; i<numleafbrushes ; i++)
 +		dleafbrushes[i] = LittleShort (dleafbrushes[i]);
 +
 +//
 +// surfedges
 +//
 +	for (i=0 ; i<numsurfedges ; i++)
 +		dsurfedges[i] = LittleLong (dsurfedges[i]);
 +
 +//
 +// edges
 +//
 +	for (i=0 ; i<numedges ; i++)
 +	{
 +		dedges[i].v[0] = LittleShort (dedges[i].v[0]);
 +		dedges[i].v[1] = LittleShort (dedges[i].v[1]);
 +	}
 +
 +//
 +// brushes
 +//
 +	for (i=0 ; i<numbrushes ; i++)
 +	{
 +		dbrushes[i].firstside = LittleLong (dbrushes[i].firstside);
 +		dbrushes[i].numsides = LittleLong (dbrushes[i].numsides);
 +		dbrushes[i].contents = LittleLong (dbrushes[i].contents);
 +	}
 +
 +//
 +// areas
 +//
 +	for (i=0 ; i<numareas ; i++)
 +	{
 +		dareas[i].numareaportals = LittleLong (dareas[i].numareaportals);
 +		dareas[i].firstareaportal = LittleLong (dareas[i].firstareaportal);
 +	}
 +
 +//
 +// areasportals
 +//
 +	for (i=0 ; i<numareaportals ; i++)
 +	{
 +		dareaportals[i].portalnum = LittleLong (dareaportals[i].portalnum);
 +		dareaportals[i].otherarea = LittleLong (dareaportals[i].otherarea);
 +	}
 +
 +//
 +// brushsides
 +//
 +	for (i=0 ; i<numbrushsides ; i++)
 +	{
 +		dbrushsides[i].planenum = LittleShort (dbrushsides[i].planenum);
 +		dbrushsides[i].texinfo = LittleShort (dbrushsides[i].texinfo);
 +	}
 +
 +//
 +// visibility
 +//
 +	if (todisk)
 +		j = dvis->numclusters;
 +	else
 +		j = LittleLong(dvis->numclusters);
 +	dvis->numclusters = LittleLong (dvis->numclusters);
 +	for (i=0 ; i<j ; i++)
 +	{
 +		dvis->bitofs[i][0] = LittleLong (dvis->bitofs[i][0]);
 +		dvis->bitofs[i][1] = LittleLong (dvis->bitofs[i][1]);
 +	}
 +} //end of the function Q2_SwapBSPFile
 +
 +
 +dheader_t	*header;
 +
 +int Q2_CopyLump (int lump, void *dest, int size, int maxsize)
 +{
 +	int		length, ofs;
 +
 +	length = header->lumps[lump].filelen;
 +	ofs = header->lumps[lump].fileofs;
 +	
 +	if (length % size)
 +		Error ("LoadBSPFile: odd lump size");
 +
 +   if ((length/size) > maxsize)
 +      Error ("Q2_LoadBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize);
 +
 +	memcpy (dest, (byte *)header + ofs, length);
 +
 +	return length / size;
 +} //end of the function Q2_CopyLump
 +
 +/*
 +=============
 +LoadBSPFile
 +=============
 +*/
 +void Q2_LoadBSPFile(char *filename, int offset, int length)
 +{
 +	int			i;
 +	
 +//
 +// load the file header
 +//
 +	LoadFile (filename, (void **)&header, offset, length);
 +
 +// swap the header
 +	for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
 +		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
 +
 +	if (header->ident != IDBSPHEADER)
 +		Error ("%s is not a IBSP file", filename);
 +	if (header->version != BSPVERSION)
 +		Error ("%s is version %i, not %i", filename, header->version, BSPVERSION);
 +
 +	nummodels = Q2_CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t), MAX_MAP_MODELS);
 +	numvertexes = Q2_CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t), MAX_MAP_VERTS);
 +	numplanes = Q2_CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t), MAX_MAP_PLANES);
 +	numleafs = Q2_CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t), MAX_MAP_LEAFS);
 +	numnodes = Q2_CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t), MAX_MAP_NODES);
 +	numtexinfo = Q2_CopyLump (LUMP_TEXINFO, texinfo, sizeof(texinfo_t), MAX_MAP_TEXINFO);
 +	numfaces = Q2_CopyLump (LUMP_FACES, dfaces, sizeof(dface_t), MAX_MAP_FACES);
 +	numleaffaces = Q2_CopyLump (LUMP_LEAFFACES, dleaffaces, sizeof(dleaffaces[0]), MAX_MAP_LEAFFACES);
 +	numleafbrushes = Q2_CopyLump (LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]), MAX_MAP_LEAFBRUSHES);
 +	numsurfedges = Q2_CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]), MAX_MAP_SURFEDGES);
 +	numedges = Q2_CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t), MAX_MAP_EDGES);
 +	numbrushes = Q2_CopyLump (LUMP_BRUSHES, dbrushes, sizeof(dbrush_t), MAX_MAP_BRUSHES);
 +	numbrushsides = Q2_CopyLump (LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t), MAX_MAP_BRUSHSIDES);
 +	numareas = Q2_CopyLump (LUMP_AREAS, dareas, sizeof(darea_t), MAX_MAP_AREAS);
 +	numareaportals = Q2_CopyLump (LUMP_AREAPORTALS, dareaportals, sizeof(dareaportal_t), MAX_MAP_AREAPORTALS);
 +
 +	visdatasize = Q2_CopyLump (LUMP_VISIBILITY, dvisdata, 1, MAX_MAP_VISIBILITY);
 +	lightdatasize = Q2_CopyLump (LUMP_LIGHTING, dlightdata, 1, MAX_MAP_LIGHTING);
 +	entdatasize = Q2_CopyLump (LUMP_ENTITIES, dentdata, 1, MAX_MAP_ENTSTRING);
 +
 +	Q2_CopyLump (LUMP_POP, dpop, 1, MAX_MAP_DPOP);
 +
 +	FreeMemory(header);		// everything has been copied out
 +		
 +//
 +// swap everything
 +//	
 +	Q2_SwapBSPFile (false);
 +
 +	Q2_FixTextureReferences();
 +} //end of the function Q2_LoadBSPFile
 +
 +
 +/*
 +=============
 +LoadBSPFileTexinfo
 +
 +Only loads the texinfo lump, so qdata can scan for textures
 +=============
 +*/
 +void	Q2_LoadBSPFileTexinfo (char *filename)
 +{
 +	int			i;
 +	FILE		*f;
 +	int		length, ofs;
 +
 +	header = GetMemory(sizeof(dheader_t));
 +
 +	f = fopen (filename, "rb");
 +	fread (header, sizeof(dheader_t), 1, f);
 +
 +// swap the header
 +	for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
 +		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
 +
 +	if (header->ident != IDBSPHEADER)
 +		Error ("%s is not a IBSP file", filename);
 +	if (header->version != BSPVERSION)
 +		Error ("%s is version %i, not %i", filename, header->version, BSPVERSION);
 +
 +
 +	length = header->lumps[LUMP_TEXINFO].filelen;
 +	ofs = header->lumps[LUMP_TEXINFO].fileofs;
 +
 +	fseek (f, ofs, SEEK_SET);
 +	fread (texinfo, length, 1, f);
 +	fclose (f);
 +
 +	numtexinfo = length / sizeof(texinfo_t);
 +
 +	FreeMemory(header);		// everything has been copied out
 +		
 +	Q2_SwapBSPFile (false);
 +} //end of the function Q2_LoadBSPFileTexinfo
 +
 +
 +//============================================================================
 +
 +FILE		*wadfile;
 +dheader_t	outheader;
 +
 +void Q2_AddLump (int lumpnum, void *data, int len)
 +{
 +	lump_t *lump;
 +
 +	lump = &header->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong( ftell(wadfile) );
 +	lump->filelen = LittleLong(len);
 +	SafeWrite (wadfile, data, (len+3)&~3);
 +} //end of the function Q2_AddLump
 +
 +/*
 +=============
 +WriteBSPFile
 +
 +Swaps the bsp file in place, so it should not be referenced again
 +=============
 +*/
 +void	Q2_WriteBSPFile (char *filename)
 +{		
 +	header = &outheader;
 +	memset (header, 0, sizeof(dheader_t));
 +	
 +	Q2_SwapBSPFile (true);
 +
 +	header->ident = LittleLong (IDBSPHEADER);
 +	header->version = LittleLong (BSPVERSION);
 +	
 +	wadfile = SafeOpenWrite (filename);
 +	SafeWrite (wadfile, header, sizeof(dheader_t));	// overwritten later
 +
 +	Q2_AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t));
 +	Q2_AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t));
 +	Q2_AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t));
 +	Q2_AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t));
 +	Q2_AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t));
 +	Q2_AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t));
 +	Q2_AddLump (LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t));
 +	Q2_AddLump (LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t));
 +	Q2_AddLump (LUMP_LEAFFACES, dleaffaces, numleaffaces*sizeof(dleaffaces[0]));
 +	Q2_AddLump (LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0]));
 +	Q2_AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0]));
 +	Q2_AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t));
 +	Q2_AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t));
 +	Q2_AddLump (LUMP_AREAS, dareas, numareas*sizeof(darea_t));
 +	Q2_AddLump (LUMP_AREAPORTALS, dareaportals, numareaportals*sizeof(dareaportal_t));
 +
 +	Q2_AddLump (LUMP_LIGHTING, dlightdata, lightdatasize);
 +	Q2_AddLump (LUMP_VISIBILITY, dvisdata, visdatasize);
 +	Q2_AddLump (LUMP_ENTITIES, dentdata, entdatasize);
 +	Q2_AddLump (LUMP_POP, dpop, sizeof(dpop));
 +	
 +	fseek (wadfile, 0, SEEK_SET);
 +	SafeWrite (wadfile, header, sizeof(dheader_t));
 +	fclose (wadfile);	
 +} //end of the function Q2_WriteBSPFile
 +
 +//============================================================================
 +
 +/*
 +=============
 +PrintBSPFileSizes
 +
 +Dumps info about current file
 +=============
 +*/
 +void Q2_PrintBSPFileSizes (void)
 +{
 +	if (!num_entities)
 +		Q2_ParseEntities();
 +
 +	printf ("%6i models       %7i\n"
 +		,nummodels, (int)(nummodels*sizeof(dmodel_t)));
 +	printf ("%6i brushes      %7i\n"
 +		,numbrushes, (int)(numbrushes*sizeof(dbrush_t)));
 +	printf ("%6i brushsides   %7i\n"
 +		,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t)));
 +	printf ("%6i planes       %7i\n"
 +		,numplanes, (int)(numplanes*sizeof(dplane_t)));
 +	printf ("%6i texinfo      %7i\n"
 +		,numtexinfo, (int)(numtexinfo*sizeof(texinfo_t)));
 +	printf ("%6i entdata      %7i\n", num_entities, entdatasize);
 +
 +	printf ("\n");
 +
 +	printf ("%6i vertexes     %7i\n"
 +		,numvertexes, (int)(numvertexes*sizeof(dvertex_t)));
 +	printf ("%6i nodes        %7i\n"
 +		,numnodes, (int)(numnodes*sizeof(dnode_t)));
 +	printf ("%6i faces        %7i\n"
 +		,numfaces, (int)(numfaces*sizeof(dface_t)));
 +	printf ("%6i leafs        %7i\n"
 +		,numleafs, (int)(numleafs*sizeof(dleaf_t)));
 +	printf ("%6i leaffaces    %7i\n"
 +		,numleaffaces, (int)(numleaffaces*sizeof(dleaffaces[0])));
 +	printf ("%6i leafbrushes  %7i\n"
 +		,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0])));
 +	printf ("%6i surfedges    %7i\n"
 +		,numsurfedges, (int)(numsurfedges*sizeof(dsurfedges[0])));
 +	printf ("%6i edges        %7i\n"
 +		,numedges, (int)(numedges*sizeof(dedge_t)));
 +//NEW
 +	printf ("%6i areas        %7i\n"
 +		,numareas, (int)(numareas*sizeof(darea_t)));
 +	printf ("%6i areaportals  %7i\n"
 +		,numareaportals, (int)(numareaportals*sizeof(dareaportal_t)));
 +//ENDNEW
 +	printf ("      lightdata    %7i\n", lightdatasize);
 +	printf ("      visdata      %7i\n", visdatasize);
 +} //end of the function Q2_PrintBSPFileSizes
 +
 +/*
 +================
 +ParseEntities
 +
 +Parses the dentdata string into entities
 +================
 +*/
 +void Q2_ParseEntities (void)
 +{
 +	script_t *script;
 +
 +	num_entities = 0;
 +	script = LoadScriptMemory(dentdata, entdatasize, "*Quake2 bsp file");
 +	SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES |
 +									SCFL_NOSTRINGESCAPECHARS);
 +
 +	while(ParseEntity(script))
 +	{
 +	} //end while
 +
 +	FreeScript(script);
 +} //end of the function Q2_ParseEntities
 +
 +
 +/*
 +================
 +UnparseEntities
 +
 +Generates the dentdata string from all the entities
 +================
 +*/
 +void Q2_UnparseEntities (void)
 +{
 +	char	*buf, *end;
 +	epair_t	*ep;
 +	char	line[2048];
 +	int		i;
 +	char	key[1024], value[1024];
 +
 +	buf = dentdata;
 +	end = buf;
 +	*end = 0;
 +	
 +	for (i=0 ; i<num_entities ; i++)
 +	{
 +		ep = entities[i].epairs;
 +		if (!ep)
 +			continue;	// ent got removed
 +		
 +		strcat (end,"{\n");
 +		end += 2;
 +				
 +		for (ep = entities[i].epairs ; ep ; ep=ep->next)
 +		{
 +			strcpy (key, ep->key);
 +			StripTrailing (key);
 +			strcpy (value, ep->value);
 +			StripTrailing (value);
 +				
 +			sprintf (line, "\"%s\" \"%s\"\n", key, value);
 +			strcat (end, line);
 +			end += strlen(line);
 +		}
 +		strcat (end,"}\n");
 +		end += 2;
 +
 +		if (end > buf + MAX_MAP_ENTSTRING)
 +			Error ("Entity text too long");
 +	}
 +	entdatasize = end - buf + 1;
 +} //end of the function Q2_UnparseEntities
 +
 diff --git a/code/bspc/l_bsp_q2.h b/code/bspc/l_bsp_q2.h new file mode 100755 index 0000000..8d937a8 --- /dev/null +++ b/code/bspc/l_bsp_q2.h @@ -0,0 +1,98 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#ifndef ME
 +#define ME
 +#endif //ME
 +
 +extern	int				nummodels;
 +extern	dmodel_t			*dmodels;//[MAX_MAP_MODELS];
 +
 +extern	int				visdatasize;
 +extern	byte				*dvisdata;//[MAX_MAP_VISIBILITY];
 +extern	dvis_t			*dvis;
 +
 +extern	int				lightdatasize;
 +extern	byte				*dlightdata;//[MAX_MAP_LIGHTING];
 +
 +extern	int				entdatasize;
 +extern	char				*dentdata;//[MAX_MAP_ENTSTRING];
 +
 +extern	int				numleafs;
 +extern	dleaf_t			*dleafs;//[MAX_MAP_LEAFS];
 +
 +extern	int				numplanes;
 +extern	dplane_t			*dplanes;//[MAX_MAP_PLANES];
 +
 +extern	int				numvertexes;
 +extern	dvertex_t		*dvertexes;//[MAX_MAP_VERTS];
 +
 +extern	int				numnodes;
 +extern	dnode_t			*dnodes;//[MAX_MAP_NODES];
 +
 +extern	int				numtexinfo;
 +extern	texinfo_t		texinfo[MAX_MAP_TEXINFO];
 +
 +extern	int				numfaces;
 +extern	dface_t			*dfaces;//[MAX_MAP_FACES];
 +
 +extern	int				numedges;
 +extern	dedge_t			*dedges;//[MAX_MAP_EDGES];
 +
 +extern	int				numleaffaces;
 +extern	unsigned short	*dleaffaces;//[MAX_MAP_LEAFFACES];
 +
 +extern	int				numleafbrushes;
 +extern	unsigned short	*dleafbrushes;//[MAX_MAP_LEAFBRUSHES];
 +
 +extern	int				numsurfedges;
 +extern	int				*dsurfedges;//[MAX_MAP_SURFEDGES];
 +
 +extern	int				numareas;
 +extern	darea_t			*dareas;//[MAX_MAP_AREAS];
 +
 +extern	int				numareaportals;
 +extern	dareaportal_t	*dareaportals;//[MAX_MAP_AREAPORTALS];
 +
 +extern	int				numbrushes;
 +extern	dbrush_t			*dbrushes;//[MAX_MAP_BRUSHES];
 +
 +extern	int				numbrushsides;
 +extern	dbrushside_t	*dbrushsides;//[MAX_MAP_BRUSHSIDES];
 +
 +extern	byte		dpop[256];
 +
 +extern	char brushsidetextured[MAX_MAP_BRUSHSIDES];
 +
 +void Q2_AllocMaxBSP(void);
 +void Q2_FreeMaxBSP(void);
 +
 +void Q2_DecompressVis(byte *in, byte *decompressed);
 +int Q2_CompressVis(byte *vis, byte *dest);
 +
 +void Q2_LoadBSPFile(char *filename, int offset, int length);
 +void Q2_LoadBSPFileTexinfo(char *filename);	// just for qdata
 +void Q2_WriteBSPFile(char *filename);
 +void Q2_PrintBSPFileSizes(void);
 +void Q2_ParseEntities(void);
 +void Q2_UnparseEntities(void);
 +
 diff --git a/code/bspc/l_bsp_q3.c b/code/bspc/l_bsp_q3.c new file mode 100755 index 0000000..19a3941 --- /dev/null +++ b/code/bspc/l_bsp_q3.c @@ -0,0 +1,824 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_mem.h"
 +#include "l_log.h"
 +#include "l_poly.h"
 +#include "../botlib/l_script.h"
 +#include "l_qfiles.h"
 +#include "l_bsp_q3.h"
 +#include "l_bsp_ent.h"
 +
 +void Q3_ParseEntities (void);
 +void Q3_PrintBSPFileSizes(void);
 +
 +void GetLeafNums (void);
 +
 +//=============================================================================
 +
 +#define WCONVEX_EPSILON		0.5
 +
 +
 +int				q3_nummodels;
 +q3_dmodel_t		*q3_dmodels;//[MAX_MAP_MODELS];
 +
 +int				q3_numShaders;
 +q3_dshader_t	*q3_dshaders;//[Q3_MAX_MAP_SHADERS];
 +
 +int				q3_entdatasize;
 +char			*q3_dentdata;//[Q3_MAX_MAP_ENTSTRING];
 +
 +int				q3_numleafs;
 +q3_dleaf_t		*q3_dleafs;//[Q3_MAX_MAP_LEAFS];
 +
 +int				q3_numplanes;
 +q3_dplane_t		*q3_dplanes;//[Q3_MAX_MAP_PLANES];
 +
 +int				q3_numnodes;
 +q3_dnode_t		*q3_dnodes;//[Q3_MAX_MAP_NODES];
 +
 +int				q3_numleafsurfaces;
 +int				*q3_dleafsurfaces;//[Q3_MAX_MAP_LEAFFACES];
 +
 +int				q3_numleafbrushes;
 +int				*q3_dleafbrushes;//[Q3_MAX_MAP_LEAFBRUSHES];
 +
 +int				q3_numbrushes;
 +q3_dbrush_t		*q3_dbrushes;//[Q3_MAX_MAP_BRUSHES];
 +
 +int				q3_numbrushsides;
 +q3_dbrushside_t	*q3_dbrushsides;//[Q3_MAX_MAP_BRUSHSIDES];
 +
 +int				q3_numLightBytes;
 +byte			*q3_lightBytes;//[Q3_MAX_MAP_LIGHTING];
 +
 +int				q3_numGridPoints;
 +byte			*q3_gridData;//[Q3_MAX_MAP_LIGHTGRID];
 +
 +int				q3_numVisBytes;
 +byte			*q3_visBytes;//[Q3_MAX_MAP_VISIBILITY];
 +
 +int				q3_numDrawVerts;
 +q3_drawVert_t	*q3_drawVerts;//[Q3_MAX_MAP_DRAW_VERTS];
 +
 +int				q3_numDrawIndexes;
 +int				*q3_drawIndexes;//[Q3_MAX_MAP_DRAW_INDEXES];
 +
 +int				q3_numDrawSurfaces;
 +q3_dsurface_t	*q3_drawSurfaces;//[Q3_MAX_MAP_DRAW_SURFS];
 +
 +int				q3_numFogs;
 +q3_dfog_t		*q3_dfogs;//[Q3_MAX_MAP_FOGS];
 +
 +char			q3_dbrushsidetextured[Q3_MAX_MAP_BRUSHSIDES];
 +
 +extern qboolean forcesidesvisible;
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_FreeMaxBSP(void)
 +{
 +	if (q3_dmodels) FreeMemory(q3_dmodels);
 +	q3_dmodels = NULL;
 +	q3_nummodels = 0;
 +	if (q3_dshaders) FreeMemory(q3_dshaders);
 +	q3_dshaders = NULL;
 +	q3_numShaders = 0;
 +	if (q3_dentdata) FreeMemory(q3_dentdata);
 +	q3_dentdata = NULL;
 +	q3_entdatasize = 0;
 +	if (q3_dleafs) FreeMemory(q3_dleafs);
 +	q3_dleafs = NULL;
 +	q3_numleafs = 0;
 +	if (q3_dplanes) FreeMemory(q3_dplanes);
 +	q3_dplanes = NULL;
 +	q3_numplanes = 0;
 +	if (q3_dnodes) FreeMemory(q3_dnodes);
 +	q3_dnodes = NULL;
 +	q3_numnodes = 0;
 +	if (q3_dleafsurfaces) FreeMemory(q3_dleafsurfaces);
 +	q3_dleafsurfaces = NULL;
 +	q3_numleafsurfaces = 0;
 +	if (q3_dleafbrushes) FreeMemory(q3_dleafbrushes);
 +	q3_dleafbrushes = NULL;
 +	q3_numleafbrushes = 0;
 +	if (q3_dbrushes) FreeMemory(q3_dbrushes);
 +	q3_dbrushes = NULL;
 +	q3_numbrushes = 0;
 +	if (q3_dbrushsides) FreeMemory(q3_dbrushsides);
 +	q3_dbrushsides = NULL;
 +	q3_numbrushsides = 0;
 +	if (q3_lightBytes) FreeMemory(q3_lightBytes);
 +	q3_lightBytes = NULL;
 +	q3_numLightBytes = 0;
 +	if (q3_gridData) FreeMemory(q3_gridData);
 +	q3_gridData = NULL;
 +	q3_numGridPoints = 0;
 +	if (q3_visBytes) FreeMemory(q3_visBytes);
 +	q3_visBytes = NULL;
 +	q3_numVisBytes = 0;
 +	if (q3_drawVerts) FreeMemory(q3_drawVerts);
 +	q3_drawVerts = NULL;
 +	q3_numDrawVerts = 0;
 +	if (q3_drawIndexes) FreeMemory(q3_drawIndexes);
 +	q3_drawIndexes = NULL;
 +	q3_numDrawIndexes = 0;
 +	if (q3_drawSurfaces) FreeMemory(q3_drawSurfaces);
 +	q3_drawSurfaces = NULL;
 +	q3_numDrawSurfaces = 0;
 +	if (q3_dfogs) FreeMemory(q3_dfogs);
 +	q3_dfogs = NULL;
 +	q3_numFogs = 0;
 +} //end of the function Q3_FreeMaxBSP
 +
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_PlaneFromPoints(vec3_t p0, vec3_t p1, vec3_t p2, vec3_t normal, float *dist)
 +{
 +	vec3_t t1, t2;
 +
 +	VectorSubtract(p0, p1, t1);
 +	VectorSubtract(p2, p1, t2);
 +	CrossProduct(t1, t2, normal);
 +	VectorNormalize(normal);
 +
 +	*dist = DotProduct(p0, normal);
 +} //end of the function PlaneFromPoints
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_SurfacePlane(q3_dsurface_t *surface, vec3_t normal, float *dist)
 +{
 +	int i;
 +	float *p0, *p1, *p2;
 +	vec3_t t1, t2;
 +
 +	p0 = q3_drawVerts[surface->firstVert].xyz;
 +	for (i = 1; i < surface->numVerts-1; i++)
 +	{
 +		p1 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz;
 +		p2 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz;
 +		VectorSubtract(p0, p1, t1);
 +		VectorSubtract(p2, p1, t2);
 +		CrossProduct(t1, t2, normal);
 +		VectorNormalize(normal);
 +		if (VectorLength(normal)) break;
 +	} //end for*/
 +/*
 +	float dot;
 +	for (i = 0; i < surface->numVerts; i++)
 +	{
 +		p0 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz;
 +		p1 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz;
 +		p2 = q3_drawVerts[surface->firstVert + ((i+2) % surface->numVerts)].xyz;
 +		VectorSubtract(p0, p1, t1);
 +		VectorSubtract(p2, p1, t2);
 +		VectorNormalize(t1);
 +		VectorNormalize(t2);
 +		dot = DotProduct(t1, t2);
 +		if (dot > -0.9 && dot < 0.9 &&
 +			VectorLength(t1) > 0.1 && VectorLength(t2) > 0.1) break;
 +	} //end for
 +	CrossProduct(t1, t2, normal);
 +	VectorNormalize(normal);
 +*/
 +	if (VectorLength(normal) < 0.9)
 +	{
 +		printf("surface %d bogus normal vector %f %f %f\n", surface - q3_drawSurfaces, normal[0], normal[1], normal[2]);
 +		printf("t1 = %f %f %f, t2 = %f %f %f\n", t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
 +		for (i = 0; i < surface->numVerts; i++)
 +		{
 +			p1 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz;
 +			Log_Print("p%d = %f %f %f\n", i, p1[0], p1[1], p1[2]);
 +		} //end for
 +	} //end if
 +	*dist = DotProduct(p0, normal);
 +} //end of the function Q3_SurfacePlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +q3_dplane_t *q3_surfaceplanes;
 +
 +void Q3_CreatePlanarSurfacePlanes(void)
 +{
 +	int i;
 +	q3_dsurface_t *surface;
 +
 +	Log_Print("creating planar surface planes...\n");
 +	q3_surfaceplanes = (q3_dplane_t *) GetClearedMemory(q3_numDrawSurfaces * sizeof(q3_dplane_t));
 +
 +	for (i = 0; i < q3_numDrawSurfaces; i++)
 +	{
 +		surface = &q3_drawSurfaces[i];
 +		if (surface->surfaceType != MST_PLANAR) continue;
 +		Q3_SurfacePlane(surface, q3_surfaceplanes[i].normal, &q3_surfaceplanes[i].dist);
 +		//Log_Print("normal = %f %f %f, dist = %f\n", q3_surfaceplanes[i].normal[0],
 +		//											q3_surfaceplanes[i].normal[1],
 +		//											q3_surfaceplanes[i].normal[2], q3_surfaceplanes[i].dist);
 +	} //end for
 +} //end of the function Q3_CreatePlanarSurfacePlanes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +/*
 +void Q3_SurfacePlane(q3_dsurface_t *surface, vec3_t normal, float *dist)
 +{
 +	//take the plane information from the lightmap vector
 +	//VectorCopy(surface->lightmapVecs[2], normal);
 +	//calculate plane dist with first surface vertex
 +	//*dist = DotProduct(q3_drawVerts[surface->firstVert].xyz, normal);
 +	Q3_PlaneFromPoints(q3_drawVerts[surface->firstVert].xyz,
 +						q3_drawVerts[surface->firstVert+1].xyz,
 +						q3_drawVerts[surface->firstVert+2].xyz, normal, dist);
 +} //end of the function Q3_SurfacePlane*/
 +//===========================================================================
 +// returns the amount the face and the winding overlap
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +float Q3_FaceOnWinding(q3_dsurface_t *surface, winding_t *winding)
 +{
 +	int i;
 +	float dist, area;
 +	q3_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	winding_t *w;
 +
 +	//copy the winding before chopping
 +	w = CopyWinding(winding);
 +	//retrieve the surface plane
 +	Q3_SurfacePlane(surface, plane.normal, &plane.dist);
 +	//chop the winding with the surface edge planes
 +	for (i = 0; i < surface->numVerts && w; i++)
 +	{
 +		v1 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz;
 +		v2 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz;
 +		//create a plane through the edge from v1 to v2, orthogonal to the
 +		//surface plane and with the normal vector pointing inward
 +		VectorSubtract(v2, v1, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		ChopWindingInPlace(&w, normal, dist, -0.1); //CLIP_EPSILON
 +	} //end for
 +	if (w)
 +	{
 +		area = WindingArea(w);
 +		FreeWinding(w);
 +		return area;
 +	} //end if
 +	return 0;
 +} //end of the function Q3_FaceOnWinding
 +//===========================================================================
 +// creates a winding for the given brush side on the given brush
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +winding_t *Q3_BrushSideWinding(q3_dbrush_t *brush, q3_dbrushside_t *baseside)
 +{
 +	int i;
 +	q3_dplane_t *baseplane, *plane;
 +	winding_t *w;
 +	q3_dbrushside_t *side;
 +	
 +	//create a winding for the brush side with the given planenumber
 +	baseplane = &q3_dplanes[baseside->planeNum];
 +	w = BaseWindingForPlane(baseplane->normal, baseplane->dist);
 +	for (i = 0; i < brush->numSides && w; i++)
 +	{
 +		side = &q3_dbrushsides[brush->firstSide + i];
 +		//don't chop with the base plane
 +		if (side->planeNum == baseside->planeNum) continue;
 +		//also don't use planes that are almost equal
 +		plane = &q3_dplanes[side->planeNum];
 +		if (DotProduct(baseplane->normal, plane->normal) > 0.999
 +				&& fabs(baseplane->dist - plane->dist) < 0.01) continue;
 +		//
 +		plane = &q3_dplanes[side->planeNum^1];
 +		ChopWindingInPlace(&w, plane->normal, plane->dist, -0.1); //CLIP_EPSILON);
 +	} //end for
 +	return w;
 +} //end of the function Q3_BrushSideWinding
 +//===========================================================================
 +// fix screwed brush texture references
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WindingIsTiny(winding_t *w);
 +
 +void Q3_FindVisibleBrushSides(void)
 +{
 +	int i, j, k, we, numtextured, numsides;
 +	float dot;
 +	q3_dplane_t *plane;
 +	q3_dbrushside_t *brushside;
 +	q3_dbrush_t *brush;
 +	q3_dsurface_t *surface;
 +	winding_t *w;
 +
 +	memset(q3_dbrushsidetextured, false, Q3_MAX_MAP_BRUSHSIDES);
 +	//
 +	numsides = 0;
 +	//create planes for the planar surfaces
 +	Q3_CreatePlanarSurfacePlanes();
 +	Log_Print("searching visible brush sides...\n");
 +	Log_Print("%6d brush sides", numsides);
 +	//go over all the brushes
 +	for (i = 0; i < q3_numbrushes; i++)
 +	{
 +		brush = &q3_dbrushes[i];
 +		//go over all the sides of the brush
 +		for (j = 0; j < brush->numSides; j++)
 +		{
 +			qprintf("\r%6d", numsides++);
 +			brushside = &q3_dbrushsides[brush->firstSide + j];
 +			//
 +			w = Q3_BrushSideWinding(brush, brushside);
 +			if (!w)
 +			{
 +				q3_dbrushsidetextured[brush->firstSide + j] = true;
 +				continue;
 +			} //end if
 +			else
 +			{
 +				//RemoveEqualPoints(w, 0.2);
 +				if (WindingIsTiny(w))
 +				{
 +					FreeWinding(w);
 +					q3_dbrushsidetextured[brush->firstSide + j] = true;
 +					continue;
 +				} //end if
 +				else
 +				{
 +					we = WindingError(w);
 +					if (we == WE_NOTENOUGHPOINTS
 +						|| we == WE_SMALLAREA
 +						|| we == WE_POINTBOGUSRANGE
 +//						|| we == WE_NONCONVEX
 +						)
 +					{
 +						FreeWinding(w);
 +						q3_dbrushsidetextured[brush->firstSide + j] = true;
 +						continue;
 +					} //end if
 +				} //end else
 +			} //end else
 +			if (WindingArea(w) < 20)
 +			{
 +				q3_dbrushsidetextured[brush->firstSide + j] = true;
 +				continue;
 +			} //end if
 +			//find a face for texturing this brush
 +			for (k = 0; k < q3_numDrawSurfaces; k++)
 +			{
 +				surface = &q3_drawSurfaces[k];
 +				if (surface->surfaceType != MST_PLANAR) continue;
 +				//
 +				//Q3_SurfacePlane(surface, plane.normal, &plane.dist);
 +				plane = &q3_surfaceplanes[k];
 +				//the surface plane and the brush side plane should be pretty much the same
 +				if (fabs(fabs(plane->dist) - fabs(q3_dplanes[brushside->planeNum].dist)) > 5) continue;
 +				dot = DotProduct(plane->normal, q3_dplanes[brushside->planeNum].normal);
 +				if (dot > -0.9 && dot < 0.9) continue;
 +				//if the face is partly or totally on the brush side
 +				if (Q3_FaceOnWinding(surface, w))
 +				{
 +					q3_dbrushsidetextured[brush->firstSide + j] = true;
 +					//Log_Write("Q3_FaceOnWinding");
 +					break;
 +				} //end if
 +			} //end for
 +			FreeWinding(w);
 +		} //end for
 +	} //end for
 +	qprintf("\r%6d brush sides\n", numsides);
 +	numtextured = 0;
 +	for (i = 0; i < q3_numbrushsides; i++)
 +	{
 +		if (forcesidesvisible) q3_dbrushsidetextured[i] = true;
 +		if (q3_dbrushsidetextured[i]) numtextured++;
 +	} //end for
 +	Log_Print("%d brush sides textured out of %d\n", numtextured, q3_numbrushsides);
 +} //end of the function Q3_FindVisibleBrushSides
 +
 +/*
 +=============
 +Q3_SwapBlock
 +
 +If all values are 32 bits, this can be used to swap everything
 +=============
 +*/
 +void Q3_SwapBlock( int *block, int sizeOfBlock ) {
 +	int		i;
 +
 +	sizeOfBlock >>= 2;
 +	for ( i = 0 ; i < sizeOfBlock ; i++ ) {
 +		block[i] = LittleLong( block[i] );
 +	}
 +} //end of the function Q3_SwapBlock
 +
 +/*
 +=============
 +Q3_SwapBSPFile
 +
 +Byte swaps all data in a bsp file.
 +=============
 +*/
 +void Q3_SwapBSPFile( void ) {
 +	int				i;
 +	
 +	// models	
 +	Q3_SwapBlock( (int *)q3_dmodels, q3_nummodels * sizeof( q3_dmodels[0] ) );
 +
 +	// shaders (don't swap the name)
 +	for ( i = 0 ; i < q3_numShaders ; i++ ) {
 +		q3_dshaders[i].contentFlags = LittleLong( q3_dshaders[i].contentFlags );
 +		q3_dshaders[i].surfaceFlags = LittleLong( q3_dshaders[i].surfaceFlags );
 +	}
 +
 +	// planes
 +	Q3_SwapBlock( (int *)q3_dplanes, q3_numplanes * sizeof( q3_dplanes[0] ) );
 +	
 +	// nodes
 +	Q3_SwapBlock( (int *)q3_dnodes, q3_numnodes * sizeof( q3_dnodes[0] ) );
 +
 +	// leafs
 +	Q3_SwapBlock( (int *)q3_dleafs, q3_numleafs * sizeof( q3_dleafs[0] ) );
 +
 +	// leaffaces
 +	Q3_SwapBlock( (int *)q3_dleafsurfaces, q3_numleafsurfaces * sizeof( q3_dleafsurfaces[0] ) );
 +
 +	// leafbrushes
 +	Q3_SwapBlock( (int *)q3_dleafbrushes, q3_numleafbrushes * sizeof( q3_dleafbrushes[0] ) );
 +
 +	// brushes
 +	Q3_SwapBlock( (int *)q3_dbrushes, q3_numbrushes * sizeof( q3_dbrushes[0] ) );
 +
 +	// brushsides
 +	Q3_SwapBlock( (int *)q3_dbrushsides, q3_numbrushsides * sizeof( q3_dbrushsides[0] ) );
 +
 +	// vis
 +	((int *)&q3_visBytes)[0] = LittleLong( ((int *)&q3_visBytes)[0] );
 +	((int *)&q3_visBytes)[1] = LittleLong( ((int *)&q3_visBytes)[1] );
 +
 +	// drawverts (don't swap colors )
 +	for ( i = 0 ; i < q3_numDrawVerts ; i++ ) {
 +		q3_drawVerts[i].lightmap[0] = LittleFloat( q3_drawVerts[i].lightmap[0] );
 +		q3_drawVerts[i].lightmap[1] = LittleFloat( q3_drawVerts[i].lightmap[1] );
 +		q3_drawVerts[i].st[0] = LittleFloat( q3_drawVerts[i].st[0] );
 +		q3_drawVerts[i].st[1] = LittleFloat( q3_drawVerts[i].st[1] );
 +		q3_drawVerts[i].xyz[0] = LittleFloat( q3_drawVerts[i].xyz[0] );
 +		q3_drawVerts[i].xyz[1] = LittleFloat( q3_drawVerts[i].xyz[1] );
 +		q3_drawVerts[i].xyz[2] = LittleFloat( q3_drawVerts[i].xyz[2] );
 +		q3_drawVerts[i].normal[0] = LittleFloat( q3_drawVerts[i].normal[0] );
 +		q3_drawVerts[i].normal[1] = LittleFloat( q3_drawVerts[i].normal[1] );
 +		q3_drawVerts[i].normal[2] = LittleFloat( q3_drawVerts[i].normal[2] );
 +	}
 +
 +	// drawindexes
 +	Q3_SwapBlock( (int *)q3_drawIndexes, q3_numDrawIndexes * sizeof( q3_drawIndexes[0] ) );
 +
 +	// drawsurfs
 +	Q3_SwapBlock( (int *)q3_drawSurfaces, q3_numDrawSurfaces * sizeof( q3_drawSurfaces[0] ) );
 +
 +	// fogs
 +	for ( i = 0 ; i < q3_numFogs ; i++ ) {
 +		q3_dfogs[i].brushNum = LittleLong( q3_dfogs[i].brushNum );
 +	}
 +}
 +
 +
 +
 +/*
 +=============
 +Q3_CopyLump
 +=============
 +*/
 +int Q3_CopyLump( q3_dheader_t	*header, int lump, void **dest, int size ) {
 +	int		length, ofs;
 +
 +	length = header->lumps[lump].filelen;
 +	ofs = header->lumps[lump].fileofs;
 +	
 +	if ( length % size ) {
 +		Error ("Q3_LoadBSPFile: odd lump size");
 +	}
 +
 +	*dest = GetMemory(length);
 +
 +	memcpy( *dest, (byte *)header + ofs, length );
 +
 +	return length / size;
 +}
 +
 +/*
 +=============
 +CountTriangles
 +=============
 +*/
 +void CountTriangles( void ) {
 +	int i, numTris, numPatchTris;
 +	q3_dsurface_t *surface;
 +
 +	numTris = numPatchTris = 0;
 +	for ( i = 0; i < q3_numDrawSurfaces; i++ ) {
 +		surface = &q3_drawSurfaces[i];
 +
 +		numTris += surface->numIndexes / 3;
 +
 +		if ( surface->patchWidth ) {
 +			numPatchTris += surface->patchWidth * surface->patchHeight * 2;
 +		}
 +	}
 +
 +	Log_Print( "%6d triangles\n", numTris );
 +	Log_Print( "%6d patch tris\n", numPatchTris );
 +}
 +
 +/*
 +=============
 +Q3_LoadBSPFile
 +=============
 +*/
 +void	Q3_LoadBSPFile(struct quakefile_s *qf)
 +{
 +	q3_dheader_t	*header;
 +
 +	// load the file header
 +	//LoadFile(filename, (void **)&header, offset, length);
 +	//
 +	LoadQuakeFile(qf, (void **)&header);
 +
 +	// swap the header
 +	Q3_SwapBlock( (int *)header, sizeof(*header) );
 +
 +	if ( header->ident != Q3_BSP_IDENT ) {
 +		Error( "%s is not a IBSP file", qf->filename );
 +	}
 +	if ( header->version != Q3_BSP_VERSION ) {
 +		Error( "%s is version %i, not %i", qf->filename, header->version, Q3_BSP_VERSION );
 +	}
 +
 +	q3_numShaders = Q3_CopyLump( header, Q3_LUMP_SHADERS, (void *) &q3_dshaders, sizeof(q3_dshader_t) );
 +	q3_nummodels = Q3_CopyLump( header, Q3_LUMP_MODELS, (void *) &q3_dmodels, sizeof(q3_dmodel_t) );
 +	q3_numplanes = Q3_CopyLump( header, Q3_LUMP_PLANES, (void *) &q3_dplanes, sizeof(q3_dplane_t) );
 +	q3_numleafs = Q3_CopyLump( header, Q3_LUMP_LEAFS, (void *) &q3_dleafs, sizeof(q3_dleaf_t) );
 +	q3_numnodes = Q3_CopyLump( header, Q3_LUMP_NODES, (void *) &q3_dnodes, sizeof(q3_dnode_t) );
 +	q3_numleafsurfaces = Q3_CopyLump( header, Q3_LUMP_LEAFSURFACES, (void *) &q3_dleafsurfaces, sizeof(q3_dleafsurfaces[0]) );
 +	q3_numleafbrushes = Q3_CopyLump( header, Q3_LUMP_LEAFBRUSHES, (void *) &q3_dleafbrushes, sizeof(q3_dleafbrushes[0]) );
 +	q3_numbrushes = Q3_CopyLump( header, Q3_LUMP_BRUSHES, (void *) &q3_dbrushes, sizeof(q3_dbrush_t) );
 +	q3_numbrushsides = Q3_CopyLump( header, Q3_LUMP_BRUSHSIDES, (void *) &q3_dbrushsides, sizeof(q3_dbrushside_t) );
 +	q3_numDrawVerts = Q3_CopyLump( header, Q3_LUMP_DRAWVERTS, (void *) &q3_drawVerts, sizeof(q3_drawVert_t) );
 +	q3_numDrawSurfaces = Q3_CopyLump( header, Q3_LUMP_SURFACES, (void *) &q3_drawSurfaces, sizeof(q3_dsurface_t) );
 +	q3_numFogs = Q3_CopyLump( header, Q3_LUMP_FOGS, (void *) &q3_dfogs, sizeof(q3_dfog_t) );
 +	q3_numDrawIndexes = Q3_CopyLump( header, Q3_LUMP_DRAWINDEXES, (void *) &q3_drawIndexes, sizeof(q3_drawIndexes[0]) );
 +
 +	q3_numVisBytes = Q3_CopyLump( header, Q3_LUMP_VISIBILITY, (void *) &q3_visBytes, 1 );
 +	q3_numLightBytes = Q3_CopyLump( header, Q3_LUMP_LIGHTMAPS, (void *) &q3_lightBytes, 1 );
 +	q3_entdatasize = Q3_CopyLump( header, Q3_LUMP_ENTITIES, (void *) &q3_dentdata, 1);
 +
 +	q3_numGridPoints = Q3_CopyLump( header, Q3_LUMP_LIGHTGRID, (void *) &q3_gridData, 8 );
 +
 +	CountTriangles();
 +
 +	FreeMemory( header );		// everything has been copied out
 +		
 +	// swap everything
 +	Q3_SwapBSPFile();
 +
 +	Q3_FindVisibleBrushSides();
 +
 +	//Q3_PrintBSPFileSizes();
 +}
 +
 +
 +//============================================================================
 +
 +/*
 +=============
 +Q3_AddLump
 +=============
 +*/
 +void Q3_AddLump( FILE *bspfile, q3_dheader_t *header, int lumpnum, void *data, int len ) {
 +	q3_lump_t *lump;
 +
 +	lump = &header->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong( ftell(bspfile) );
 +	lump->filelen = LittleLong( len );
 +	SafeWrite( bspfile, data, (len+3)&~3 );
 +}
 +
 +/*
 +=============
 +Q3_WriteBSPFile
 +
 +Swaps the bsp file in place, so it should not be referenced again
 +=============
 +*/
 +void	Q3_WriteBSPFile( char *filename )
 +{
 +	q3_dheader_t	outheader, *header;
 +	FILE		*bspfile;
 +
 +	header = &outheader;
 +	memset( header, 0, sizeof(q3_dheader_t) );
 +	
 +	Q3_SwapBSPFile();
 +
 +	header->ident = LittleLong( Q3_BSP_IDENT );
 +	header->version = LittleLong( Q3_BSP_VERSION );
 +	
 +	bspfile = SafeOpenWrite( filename );
 +	SafeWrite( bspfile, header, sizeof(q3_dheader_t) );	// overwritten later
 +
 +	Q3_AddLump( bspfile, header, Q3_LUMP_SHADERS, q3_dshaders, q3_numShaders*sizeof(q3_dshader_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_PLANES, q3_dplanes, q3_numplanes*sizeof(q3_dplane_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_LEAFS, q3_dleafs, q3_numleafs*sizeof(q3_dleaf_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_NODES, q3_dnodes, q3_numnodes*sizeof(q3_dnode_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_BRUSHES, q3_dbrushes, q3_numbrushes*sizeof(q3_dbrush_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_BRUSHSIDES, q3_dbrushsides, q3_numbrushsides*sizeof(q3_dbrushside_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_LEAFSURFACES, q3_dleafsurfaces, q3_numleafsurfaces*sizeof(q3_dleafsurfaces[0]) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_LEAFBRUSHES, q3_dleafbrushes, q3_numleafbrushes*sizeof(q3_dleafbrushes[0]) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_MODELS, q3_dmodels, q3_nummodels*sizeof(q3_dmodel_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_DRAWVERTS, q3_drawVerts, q3_numDrawVerts*sizeof(q3_drawVert_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_SURFACES, q3_drawSurfaces, q3_numDrawSurfaces*sizeof(q3_dsurface_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_VISIBILITY, q3_visBytes, q3_numVisBytes );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_LIGHTMAPS, q3_lightBytes, q3_numLightBytes );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_LIGHTGRID, q3_gridData, 8 * q3_numGridPoints );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_ENTITIES, q3_dentdata, q3_entdatasize );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_FOGS, q3_dfogs, q3_numFogs * sizeof(q3_dfog_t) );
 +	Q3_AddLump( bspfile, header, Q3_LUMP_DRAWINDEXES, q3_drawIndexes, q3_numDrawIndexes * sizeof(q3_drawIndexes[0]) );
 +	
 +	fseek (bspfile, 0, SEEK_SET);
 +	SafeWrite (bspfile, header, sizeof(q3_dheader_t));
 +	fclose (bspfile);	
 +}
 +
 +//============================================================================
 +
 +/*
 +=============
 +Q3_PrintBSPFileSizes
 +
 +Dumps info about current file
 +=============
 +*/
 +void Q3_PrintBSPFileSizes( void )
 +{
 +	if ( !num_entities )
 +	{
 +		Q3_ParseEntities();
 +	}
 +
 +	Log_Print ("%6i models       %7i\n"
 +		,q3_nummodels, (int)(q3_nummodels*sizeof(q3_dmodel_t)));
 +	Log_Print ("%6i shaders      %7i\n"
 +		,q3_numShaders, (int)(q3_numShaders*sizeof(q3_dshader_t)));
 +	Log_Print ("%6i brushes      %7i\n"
 +		,q3_numbrushes, (int)(q3_numbrushes*sizeof(q3_dbrush_t)));
 +	Log_Print ("%6i brushsides   %7i\n"
 +		,q3_numbrushsides, (int)(q3_numbrushsides*sizeof(q3_dbrushside_t)));
 +	Log_Print ("%6i fogs         %7i\n"
 +		,q3_numFogs, (int)(q3_numFogs*sizeof(q3_dfog_t)));
 +	Log_Print ("%6i planes       %7i\n"
 +		,q3_numplanes, (int)(q3_numplanes*sizeof(q3_dplane_t)));
 +	Log_Print ("%6i entdata      %7i\n", num_entities, q3_entdatasize);
 +
 +	Log_Print ("\n");
 +
 +	Log_Print ("%6i nodes        %7i\n"
 +		,q3_numnodes, (int)(q3_numnodes*sizeof(q3_dnode_t)));
 +	Log_Print ("%6i leafs        %7i\n"
 +		,q3_numleafs, (int)(q3_numleafs*sizeof(q3_dleaf_t)));
 +	Log_Print ("%6i leafsurfaces %7i\n"
 +		,q3_numleafsurfaces, (int)(q3_numleafsurfaces*sizeof(q3_dleafsurfaces[0])));
 +	Log_Print ("%6i leafbrushes  %7i\n"
 +		,q3_numleafbrushes, (int)(q3_numleafbrushes*sizeof(q3_dleafbrushes[0])));
 +	Log_Print ("%6i drawverts    %7i\n"
 +		,q3_numDrawVerts, (int)(q3_numDrawVerts*sizeof(q3_drawVerts[0])));
 +	Log_Print ("%6i drawindexes  %7i\n"
 +		,q3_numDrawIndexes, (int)(q3_numDrawIndexes*sizeof(q3_drawIndexes[0])));
 +	Log_Print ("%6i drawsurfaces %7i\n"
 +		,q3_numDrawSurfaces, (int)(q3_numDrawSurfaces*sizeof(q3_drawSurfaces[0])));
 +
 +	Log_Print ("%6i lightmaps    %7i\n"
 +		,q3_numLightBytes / (LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT*3), q3_numLightBytes );
 +	Log_Print ("       visibility   %7i\n"
 +		, q3_numVisBytes );
 +}
 +
 +/*
 +================
 +Q3_ParseEntities
 +
 +Parses the q3_dentdata string into entities
 +================
 +*/
 +void Q3_ParseEntities (void)
 +{
 +	script_t *script;
 +
 +	num_entities = 0;
 +	script = LoadScriptMemory(q3_dentdata, q3_entdatasize, "*Quake3 bsp file");
 +	SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES |
 +									SCFL_NOSTRINGESCAPECHARS);
 +
 +	while(ParseEntity(script))
 +	{
 +	} //end while
 +
 +	FreeScript(script);
 +} //end of the function Q3_ParseEntities
 +
 +
 +/*
 +================
 +Q3_UnparseEntities
 +
 +Generates the q3_dentdata string from all the entities
 +================
 +*/
 +void Q3_UnparseEntities (void)
 +{
 +	char *buf, *end;
 +	epair_t *ep;
 +	char line[2048];
 +	int i;
 +	
 +	buf = q3_dentdata;
 +	end = buf;
 +	*end = 0;
 +	
 +	for (i=0 ; i<num_entities ; i++)
 +	{
 +		ep = entities[i].epairs;
 +		if (!ep)
 +			continue;	// ent got removed
 +		
 +		strcat (end,"{\n");
 +		end += 2;
 +				
 +		for (ep = entities[i].epairs ; ep ; ep=ep->next)
 +		{
 +			sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
 +			strcat (end, line);
 +			end += strlen(line);
 +		}
 +		strcat (end,"}\n");
 +		end += 2;
 +
 +		if (end > buf + Q3_MAX_MAP_ENTSTRING)
 +			Error ("Entity text too long");
 +	}
 +	q3_entdatasize = end - buf + 1;
 +} //end of the function Q3_UnparseEntities
 +
 +
 diff --git a/code/bspc/l_bsp_q3.h b/code/bspc/l_bsp_q3.h new file mode 100755 index 0000000..6ac175c --- /dev/null +++ b/code/bspc/l_bsp_q3.h @@ -0,0 +1,81 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "q3files.h"
 +//#include "surfaceflags.h"
 +
 +extern	int				q3_nummodels;
 +extern	q3_dmodel_t		*q3_dmodels;//[MAX_MAP_MODELS];
 +
 +extern	int				q3_numShaders;
 +extern	q3_dshader_t	*q3_dshaders;//[Q3_MAX_MAP_SHADERS];
 +
 +extern	int				q3_entdatasize;
 +extern	char			*q3_dentdata;//[Q3_MAX_MAP_ENTSTRING];
 +
 +extern	int				q3_numleafs;
 +extern	q3_dleaf_t		*q3_dleafs;//[Q3_MAX_MAP_LEAFS];
 +
 +extern	int				q3_numplanes;
 +extern	q3_dplane_t		*q3_dplanes;//[Q3_MAX_MAP_PLANES];
 +
 +extern	int				q3_numnodes;
 +extern	q3_dnode_t		*q3_dnodes;//[Q3_MAX_MAP_NODES];
 +
 +extern	int				q3_numleafsurfaces;
 +extern	int				*q3_dleafsurfaces;//[Q3_MAX_MAP_LEAFFACES];
 +
 +extern	int				q3_numleafbrushes;
 +extern	int				*q3_dleafbrushes;//[Q3_MAX_MAP_LEAFBRUSHES];
 +
 +extern	int				q3_numbrushes;
 +extern	q3_dbrush_t		*q3_dbrushes;//[Q3_MAX_MAP_BRUSHES];
 +
 +extern	int				q3_numbrushsides;
 +extern	q3_dbrushside_t	*q3_dbrushsides;//[Q3_MAX_MAP_BRUSHSIDES];
 +
 +extern	int				q3_numLightBytes;
 +extern	byte			*q3_lightBytes;//[Q3_MAX_MAP_LIGHTING];
 +
 +extern	int				q3_numGridPoints;
 +extern	byte			*q3_gridData;//[Q3_MAX_MAP_LIGHTGRID];
 +
 +extern	int				q3_numVisBytes;
 +extern	byte			*q3_visBytes;//[Q3_MAX_MAP_VISIBILITY];
 +
 +extern	int				q3_numDrawVerts;
 +extern	q3_drawVert_t	*q3_drawVerts;//[Q3_MAX_MAP_DRAW_VERTS];
 +
 +extern	int				q3_numDrawIndexes;
 +extern	int				*q3_drawIndexes;//[Q3_MAX_MAP_DRAW_INDEXES];
 +
 +extern	int				q3_numDrawSurfaces;
 +extern	q3_dsurface_t	*q3_drawSurfaces;//[Q3_MAX_MAP_DRAW_SURFS];
 +
 +extern	int				q3_numFogs;
 +extern	q3_dfog_t		*q3_dfogs;//[Q3_MAX_MAP_FOGS];
 +
 +extern	char			q3_dbrushsidetextured[Q3_MAX_MAP_BRUSHSIDES];
 +
 +void Q3_LoadBSPFile(struct quakefile_s *qf);
 +void Q3_FreeMaxBSP(void);
 +void Q3_ParseEntities (void);
 diff --git a/code/bspc/l_bsp_sin.c b/code/bspc/l_bsp_sin.c new file mode 100755 index 0000000..de51528 --- /dev/null +++ b/code/bspc/l_bsp_sin.c @@ -0,0 +1,1186 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_mem.h"
 +#include "l_log.h"
 +#include "l_poly.h"
 +#include "../botlib/l_script.h"
 +#include "l_bsp_ent.h"
 +#include "l_bsp_sin.h"
 +
 +void GetLeafNums (void);
 +
 +//=============================================================================
 +
 +int					sin_nummodels;
 +sin_dmodel_t		*sin_dmodels;//[SIN_MAX_MAP_MODELS];
 +
 +int					sin_visdatasize;
 +byte				*sin_dvisdata;//[SIN_MAX_MAP_VISIBILITY];
 +sin_dvis_t			*sin_dvis;// = (sin_dvis_t *)sin_sin_dvisdata;
 +
 +int					sin_lightdatasize;
 +byte				*sin_dlightdata;//[SIN_MAX_MAP_LIGHTING];
 +
 +int					sin_entdatasize;
 +char				*sin_dentdata;//[SIN_MAX_MAP_ENTSTRING];
 +
 +int					sin_numleafs;
 +sin_dleaf_t			*sin_dleafs;//[SIN_MAX_MAP_LEAFS];
 +
 +int					sin_numplanes;
 +sin_dplane_t		*sin_dplanes;//[SIN_MAX_MAP_PLANES];
 +
 +int					sin_numvertexes;
 +sin_dvertex_t		*sin_dvertexes;//[SIN_MAX_MAP_VERTS];
 +
 +int					sin_numnodes;
 +sin_dnode_t			*sin_dnodes;//[SIN_MAX_MAP_NODES];
 +
 +int					sin_numtexinfo;
 +sin_texinfo_t		*sin_texinfo;//[SIN_MAX_MAP_sin_texinfo];
 +
 +int					sin_numfaces;
 +sin_dface_t			*sin_dfaces;//[SIN_MAX_MAP_FACES];
 +
 +int					sin_numedges;
 +sin_dedge_t			*sin_dedges;//[SIN_MAX_MAP_EDGES];
 +
 +int					sin_numleaffaces;
 +unsigned short		*sin_dleaffaces;//[SIN_MAX_MAP_LEAFFACES];
 +
 +int					sin_numleafbrushes;
 +unsigned short		*sin_dleafbrushes;//[SIN_MAX_MAP_LEAFBRUSHES];
 +
 +int					sin_numsurfedges;
 +int					*sin_dsurfedges;//[SIN_MAX_MAP_SURFEDGES];
 +
 +int					sin_numbrushes;
 +sin_dbrush_t		*sin_dbrushes;//[SIN_MAX_MAP_BRUSHES];
 +
 +int					sin_numbrushsides;
 +sin_dbrushside_t	*sin_dbrushsides;//[SIN_MAX_MAP_BRUSHSIDES];
 +
 +int					sin_numareas;
 +sin_darea_t			*sin_dareas;//[SIN_MAX_MAP_AREAS];
 +
 +int					sin_numareaportals;
 +sin_dareaportal_t	*sin_dareaportals;//[SIN_MAX_MAP_AREAPORTALS];
 +
 +int					sin_numlightinfo;
 +sin_lightvalue_t	*sin_lightinfo;//[SIN_MAX_MAP_LIGHTINFO];
 +
 +byte				sin_dpop[256];
 +
 +char				sin_dbrushsidetextured[SIN_MAX_MAP_BRUSHSIDES];
 +
 +int sin_bspallocated = false;
 +int sin_allocatedbspmem = 0;
 +
 +void Sin_AllocMaxBSP(void)
 +{
 +	//models
 +	sin_nummodels = 0;
 +	sin_dmodels = (sin_dmodel_t *) GetClearedMemory(SIN_MAX_MAP_MODELS * sizeof(sin_dmodel_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_MODELS * sizeof(sin_dmodel_t);
 +	//vis data
 +	sin_visdatasize = 0;
 +	sin_dvisdata = (byte *) GetClearedMemory(SIN_MAX_MAP_VISIBILITY * sizeof(byte));
 +	sin_dvis = (sin_dvis_t *) sin_dvisdata;
 +	sin_allocatedbspmem += SIN_MAX_MAP_VISIBILITY * sizeof(byte);
 +	//light data
 +	sin_lightdatasize = 0;
 +	sin_dlightdata = (byte *) GetClearedMemory(SIN_MAX_MAP_LIGHTING * sizeof(byte));
 +	sin_allocatedbspmem += SIN_MAX_MAP_LIGHTING * sizeof(byte);
 +	//entity data
 +	sin_entdatasize = 0;
 +	sin_dentdata = (char *) GetClearedMemory(SIN_MAX_MAP_ENTSTRING * sizeof(char));
 +	sin_allocatedbspmem += SIN_MAX_MAP_ENTSTRING * sizeof(char);
 +	//leafs
 +	sin_numleafs = 0;
 +	sin_dleafs = (sin_dleaf_t *) GetClearedMemory(SIN_MAX_MAP_LEAFS * sizeof(sin_dleaf_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_LEAFS * sizeof(sin_dleaf_t);
 +	//planes
 +	sin_numplanes = 0;
 +	sin_dplanes = (sin_dplane_t *) GetClearedMemory(SIN_MAX_MAP_PLANES * sizeof(sin_dplane_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_PLANES * sizeof(sin_dplane_t);
 +	//vertexes
 +	sin_numvertexes = 0;
 +	sin_dvertexes = (sin_dvertex_t *) GetClearedMemory(SIN_MAX_MAP_VERTS * sizeof(sin_dvertex_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_VERTS * sizeof(sin_dvertex_t);
 +	//nodes
 +	sin_numnodes = 0;
 +	sin_dnodes = (sin_dnode_t *) GetClearedMemory(SIN_MAX_MAP_NODES * sizeof(sin_dnode_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_NODES * sizeof(sin_dnode_t);
 +	//texture info
 +	sin_numtexinfo = 0;
 +	sin_texinfo = (sin_texinfo_t *) GetClearedMemory(SIN_MAX_MAP_TEXINFO * sizeof(sin_texinfo_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_TEXINFO * sizeof(sin_texinfo_t);
 +	//faces
 +	sin_numfaces = 0;
 +	sin_dfaces = (sin_dface_t *) GetClearedMemory(SIN_MAX_MAP_FACES * sizeof(sin_dface_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_FACES * sizeof(sin_dface_t);
 +	//edges
 +	sin_numedges = 0;
 +	sin_dedges = (sin_dedge_t *) GetClearedMemory(SIN_MAX_MAP_EDGES * sizeof(sin_dedge_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_EDGES * sizeof(sin_dedge_t);
 +	//leaf faces
 +	sin_numleaffaces = 0;
 +	sin_dleaffaces = (unsigned short *) GetClearedMemory(SIN_MAX_MAP_LEAFFACES * sizeof(unsigned short));
 +	sin_allocatedbspmem += SIN_MAX_MAP_LEAFFACES * sizeof(unsigned short);
 +	//leaf brushes
 +	sin_numleafbrushes = 0;
 +	sin_dleafbrushes = (unsigned short *) GetClearedMemory(SIN_MAX_MAP_LEAFBRUSHES * sizeof(unsigned short));
 +	sin_allocatedbspmem += SIN_MAX_MAP_LEAFBRUSHES * sizeof(unsigned short);
 +	//surface edges
 +	sin_numsurfedges = 0;
 +	sin_dsurfedges = (int *) GetClearedMemory(SIN_MAX_MAP_SURFEDGES * sizeof(int));
 +	sin_allocatedbspmem += SIN_MAX_MAP_SURFEDGES * sizeof(int);
 +	//brushes
 +	sin_numbrushes = 0;
 +	sin_dbrushes = (sin_dbrush_t *) GetClearedMemory(SIN_MAX_MAP_BRUSHES * sizeof(sin_dbrush_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_BRUSHES * sizeof(sin_dbrush_t);
 +	//brushsides
 +	sin_numbrushsides = 0;
 +	sin_dbrushsides = (sin_dbrushside_t *) GetClearedMemory(SIN_MAX_MAP_BRUSHSIDES * sizeof(sin_dbrushside_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_BRUSHSIDES * sizeof(sin_dbrushside_t);
 +	//areas
 +	sin_numareas = 0;
 +	sin_dareas = (sin_darea_t *) GetClearedMemory(SIN_MAX_MAP_AREAS * sizeof(sin_darea_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_AREAS * sizeof(sin_darea_t);
 +	//area portals
 +	sin_numareaportals = 0;
 +	sin_dareaportals = (sin_dareaportal_t *) GetClearedMemory(SIN_MAX_MAP_AREAPORTALS * sizeof(sin_dareaportal_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_AREAPORTALS * sizeof(sin_dareaportal_t);
 +	//light info
 +	sin_numlightinfo = 0;
 +	sin_lightinfo = (sin_lightvalue_t *) GetClearedMemory(SIN_MAX_MAP_LIGHTINFO * sizeof(sin_lightvalue_t));
 +	sin_allocatedbspmem += SIN_MAX_MAP_LIGHTINFO * sizeof(sin_lightvalue_t);
 +	//print allocated memory
 +	Log_Print("allocated ");
 +	PrintMemorySize(sin_allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +} //end of the function Sin_AllocMaxBSP
 +
 +void Sin_FreeMaxBSP(void)
 +{
 +	//models
 +	sin_nummodels = 0;
 +	FreeMemory(sin_dmodels);
 +	sin_dmodels = NULL;
 +	//vis data
 +	sin_visdatasize = 0;
 +	FreeMemory(sin_dvisdata);
 +	sin_dvisdata = NULL;
 +	sin_dvis = NULL;
 +	//light data
 +	sin_lightdatasize = 0;
 +	FreeMemory(sin_dlightdata);
 +	sin_dlightdata = NULL;
 +	//entity data
 +	sin_entdatasize = 0;
 +	FreeMemory(sin_dentdata);
 +	sin_dentdata = NULL;
 +	//leafs
 +	sin_numleafs = 0;
 +	FreeMemory(sin_dleafs);
 +	sin_dleafs = NULL;
 +	//planes
 +	sin_numplanes = 0;
 +	FreeMemory(sin_dplanes);
 +	sin_dplanes = NULL;
 +	//vertexes
 +	sin_numvertexes = 0;
 +	FreeMemory(sin_dvertexes);
 +	sin_dvertexes = NULL;
 +	//nodes
 +	sin_numnodes = 0;
 +	FreeMemory(sin_dnodes);
 +	sin_dnodes = NULL;
 +	//texture info
 +	sin_numtexinfo = 0;
 +	FreeMemory(sin_texinfo);
 +	sin_texinfo = NULL;
 +	//faces
 +	sin_numfaces = 0;
 +	FreeMemory(sin_dfaces);
 +	sin_dfaces = NULL;
 +	//edges
 +	sin_numedges = 0;
 +	FreeMemory(sin_dedges);
 +	sin_dedges = NULL;
 +	//leaf faces
 +	sin_numleaffaces = 0;
 +	FreeMemory(sin_dleaffaces);
 +	sin_dleaffaces = NULL;
 +	//leaf brushes
 +	sin_numleafbrushes = 0;
 +	FreeMemory(sin_dleafbrushes);
 +	sin_dleafbrushes = NULL;
 +	//surface edges
 +	sin_numsurfedges = 0;
 +	FreeMemory(sin_dsurfedges);
 +	sin_dsurfedges = NULL;
 +	//brushes
 +	sin_numbrushes = 0;
 +	FreeMemory(sin_dbrushes);
 +	sin_dbrushes = NULL;
 +	//brushsides
 +	sin_numbrushsides = 0;
 +	FreeMemory(sin_dbrushsides);
 +	sin_dbrushsides = NULL;
 +	//areas
 +	sin_numareas = 0;
 +	FreeMemory(sin_dareas);
 +	sin_dareas = NULL;
 +	//area portals
 +	sin_numareaportals = 0;
 +	FreeMemory(sin_dareaportals);
 +	sin_dareaportals = NULL;
 +	//light info
 +	sin_numlightinfo = 0;
 +	FreeMemory(sin_lightinfo);
 +	sin_lightinfo = NULL;
 +	//
 +	Log_Print("freed ");
 +	PrintMemorySize(sin_allocatedbspmem);
 +	Log_Print(" of BSP memory\n");
 +	sin_allocatedbspmem = 0;
 +} //end of the function Sin_FreeMaxBSP
 +
 +#define WCONVEX_EPSILON		0.5
 +
 +//===========================================================================
 +// returns the amount the face and the winding overlap
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +float Sin_FaceOnWinding(sin_dface_t *face, winding_t *winding)
 +{
 +	int i, edgenum, side;
 +	float dist, area;
 +	sin_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	winding_t *w;
 +
 +	//
 +	w = CopyWinding(winding);
 +	memcpy(&plane, &sin_dplanes[face->planenum], sizeof(sin_dplane_t));
 +	//check on which side of the plane the face is
 +	if (face->side)
 +	{
 +		VectorNegate(plane.normal, plane.normal);
 +		plane.dist = -plane.dist;
 +	} //end if
 +	for (i = 0; i < face->numedges && w; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = sin_dsurfedges[face->firstedge + i];
 +		side = edgenum > 0;
 +		//if the face plane is flipped
 +		v1 = sin_dvertexes[sin_dedges[abs(edgenum)].v[side]].point;
 +		v2 = sin_dvertexes[sin_dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing out of the face
 +		VectorSubtract(v1, v2, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON
 +	} //end for
 +	if (w)
 +	{
 +		area = WindingArea(w);
 +		FreeWinding(w);
 +		return area;
 +	} //end if
 +	return 0;
 +} //end of the function Sin_FaceOnWinding
 +//===========================================================================
 +// creates a winding for the given brush side on the given brush
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +winding_t *Sin_BrushSideWinding(sin_dbrush_t *brush, sin_dbrushside_t *baseside)
 +{
 +	int i;
 +	sin_dplane_t *baseplane, *plane;
 +	sin_dbrushside_t *side;
 +	winding_t *w;
 +	
 +	//create a winding for the brush side with the given planenumber
 +	baseplane = &sin_dplanes[baseside->planenum];
 +	w = BaseWindingForPlane(baseplane->normal, baseplane->dist);
 +	for (i = 0; i < brush->numsides && w; i++)
 +	{
 +		side = &sin_dbrushsides[brush->firstside + i];
 +		//don't chop with the base plane
 +		if (side->planenum == baseside->planenum) continue;
 +		//also don't use planes that are almost equal
 +		plane = &sin_dplanes[side->planenum];
 +		if (DotProduct(baseplane->normal, plane->normal) > 0.999
 +				&& fabs(baseplane->dist - plane->dist) < 0.01) continue;
 +		//
 +		plane = &sin_dplanes[side->planenum^1];
 +		ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
 +	} //end for
 +	return w;
 +} //end of the function Sin_BrushSideWinding
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Sin_HintSkipBrush(sin_dbrush_t *brush)
 +{
 +	int j;
 +	sin_dbrushside_t *brushside;
 +
 +	for (j = 0; j < brush->numsides; j++)
 +	{
 +		brushside = &sin_dbrushsides[brush->firstside + j];
 +		if (brushside->texinfo > 0)
 +		{
 +			if (sin_texinfo[brushside->texinfo].flags & (SURF_SKIP|SURF_HINT))
 +			{
 +				return true;
 +			} //end if
 +		} //end if
 +	} //end for
 +	return false;
 +} //end of the function Sin_HintSkipBrush
 +//===========================================================================
 +// fix screwed brush texture references
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WindingIsTiny(winding_t *w);
 +
 +void Sin_FixTextureReferences(void)
 +{
 +	int i, j, k, we;
 +	sin_dbrushside_t *brushside;
 +	sin_dbrush_t *brush;
 +	sin_dface_t *face;
 +	winding_t *w;
 +
 +	memset(sin_dbrushsidetextured, false, SIN_MAX_MAP_BRUSHSIDES);
 +	//go over all the brushes
 +   for (i = 0; i < sin_numbrushes; i++)
 +   {
 +		brush = &sin_dbrushes[i];
 +		//hint brushes are not textured
 +		if (Sin_HintSkipBrush(brush)) continue;
 +		//go over all the sides of the brush
 +		for (j = 0; j < brush->numsides; j++)
 +		{
 +			brushside = &sin_dbrushsides[brush->firstside + j];
 +			//
 +			w = Sin_BrushSideWinding(brush, brushside);
 +			if (!w)
 +			{
 +				sin_dbrushsidetextured[brush->firstside + j] = true;
 +				continue;
 +			} //end if
 +			else
 +			{
 +				//RemoveEqualPoints(w, 0.2);
 +				if (WindingIsTiny(w))
 +				{
 +					FreeWinding(w);
 +					sin_dbrushsidetextured[brush->firstside + j] = true;
 +					continue;
 +				} //end if
 +				else
 +				{
 +					we = WindingError(w);
 +					if (we == WE_NOTENOUGHPOINTS
 +						|| we == WE_SMALLAREA
 +						|| we == WE_POINTBOGUSRANGE
 +//						|| we == WE_NONCONVEX
 +						)
 +					{
 +						FreeWinding(w);
 +						sin_dbrushsidetextured[brush->firstside + j] = true;
 +						continue;
 +					} //end if
 +				} //end else
 +			} //end else
 +			if (WindingArea(w) < 20)
 +			{
 +				sin_dbrushsidetextured[brush->firstside + j] = true;
 +			} //end if
 +			//find a face for texturing this brush
 +			for (k = 0; k < sin_numfaces; k++)
 +			{
 +				face = &sin_dfaces[k];
 +				//if the face is in the same plane as the brush side
 +				if ((face->planenum&~1) != (brushside->planenum&~1)) continue;
 +				//if the face is partly or totally on the brush side
 +				if (Sin_FaceOnWinding(face, w))
 +				{
 +					brushside->texinfo = face->texinfo;
 +					sin_dbrushsidetextured[brush->firstside + j] = true;
 +					break;
 +				} //end if
 +			} //end for
 +			FreeWinding(w);
 +		} //end for
 +	} //end for
 +} //end of the function Sin_FixTextureReferences*/
 +
 +/*
 +===============
 +CompressVis
 +
 +===============
 +*/
 +int Sin_CompressVis (byte *vis, byte *dest)
 +{
 +	int		j;
 +	int		rep;
 +	int		visrow;
 +	byte	*dest_p;
 +	
 +	dest_p = dest;
 +//	visrow = (r_numvisleafs + 7)>>3;
 +	visrow = (sin_dvis->numclusters + 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;
 +} //end of the function Sin_CompressVis
 +
 +
 +/*
 +===================
 +DecompressVis
 +===================
 +*/
 +void Sin_DecompressVis (byte *in, byte *decompressed)
 +{
 +	int		c;
 +	byte	*out;
 +	int		row;
 +
 +//	row = (r_numvisleafs+7)>>3;	
 +	row = (sin_dvis->numclusters+7)>>3;	
 +	out = decompressed;
 +
 +	do
 +	{
 +		if (*in)
 +		{
 +			*out++ = *in++;
 +			continue;
 +		}
 +	
 +		c = in[1];
 +		if (!c)
 +			Error ("DecompressVis: 0 repeat");
 +		in += 2;
 +		while (c)
 +		{
 +			*out++ = 0;
 +			c--;
 +		}
 +	} while (out - decompressed < row);
 +} //end of the function Sin_DecompressVis
 +
 +//=============================================================================
 +
 +/*
 +=============
 +Sin_SwapBSPFile
 +
 +Byte swaps all data in a bsp file.
 +=============
 +*/
 +void Sin_SwapBSPFile (qboolean todisk)
 +{
 +	int				i, j;
 +	sin_dmodel_t		*d;
 +
 +	
 +// models	
 +	for (i=0 ; i<sin_nummodels ; i++)
 +	{
 +		d = &sin_dmodels[i];
 +
 +		d->firstface = LittleLong (d->firstface);
 +		d->numfaces = LittleLong (d->numfaces);
 +		d->headnode = LittleLong (d->headnode);
 +		
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			d->mins[j] = LittleFloat(d->mins[j]);
 +			d->maxs[j] = LittleFloat(d->maxs[j]);
 +			d->origin[j] = LittleFloat(d->origin[j]);
 +		}
 +	}
 +
 +//
 +// vertexes
 +//
 +	for (i=0 ; i<sin_numvertexes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			sin_dvertexes[i].point[j] = LittleFloat (sin_dvertexes[i].point[j]);
 +	}
 +		
 +//
 +// planes
 +//	
 +	for (i=0 ; i<sin_numplanes ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +			sin_dplanes[i].normal[j] = LittleFloat (sin_dplanes[i].normal[j]);
 +		sin_dplanes[i].dist = LittleFloat (sin_dplanes[i].dist);
 +		sin_dplanes[i].type = LittleLong (sin_dplanes[i].type);
 +	}
 +	
 +//
 +// sin_texinfos
 +//	
 +	for (i = 0; i < sin_numtexinfo; i++)
 +	{
 +		for (j=0 ; j<8 ; j++)
 +			sin_texinfo[i].vecs[0][j] = LittleFloat (sin_texinfo[i].vecs[0][j]);
 +#ifdef SIN
 +      sin_texinfo[i].trans_mag = LittleFloat( sin_texinfo[i].trans_mag );     
 +      sin_texinfo[i].trans_angle = LittleLong( sin_texinfo[i].trans_angle );     
 +      sin_texinfo[i].animtime = LittleFloat( sin_texinfo[i].animtime );     
 +      sin_texinfo[i].nonlit = LittleFloat( sin_texinfo[i].nonlit );     
 +      sin_texinfo[i].translucence = LittleFloat( sin_texinfo[i].translucence );     
 +      sin_texinfo[i].friction = LittleFloat( sin_texinfo[i].friction );     
 +      sin_texinfo[i].restitution = LittleFloat( sin_texinfo[i].restitution );     
 +		sin_texinfo[i].flags = LittleUnsigned (sin_texinfo[i].flags);
 +#else
 +		sin_texinfo[i].value = LittleLong (sin_texinfo[i].value);
 +		sin_texinfo[i].flags = LittleLong (sin_texinfo[i].flags);
 +#endif
 +		sin_texinfo[i].nexttexinfo = LittleLong (sin_texinfo[i].nexttexinfo);
 +	}
 +
 +#ifdef SIN
 +//
 +// lightinfos
 +//	
 +	for (i = 0; i < sin_numlightinfo; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +         {
 +			sin_lightinfo[i].color[j] = LittleFloat (sin_lightinfo[i].color[j]);
 +         }
 +		sin_lightinfo[i].value = LittleLong (sin_lightinfo[i].value);
 +      sin_lightinfo[i].direct = LittleFloat( sin_lightinfo[i].direct );     
 +      sin_lightinfo[i].directangle = LittleFloat( sin_lightinfo[i].directangle );     
 +      sin_lightinfo[i].directstyle = LittleFloat( sin_lightinfo[i].directstyle );     
 +	}
 +#endif
 +	
 +//
 +// faces
 +//
 +	for (i=0 ; i<sin_numfaces ; i++)
 +	{
 +		sin_dfaces[i].texinfo = LittleShort (sin_dfaces[i].texinfo);
 +#ifdef SIN
 +		sin_dfaces[i].lightinfo = LittleLong (sin_dfaces[i].lightinfo);
 +		sin_dfaces[i].planenum = LittleUnsignedShort (sin_dfaces[i].planenum);
 +#else
 +		sin_dfaces[i].planenum = LittleShort (sin_dfaces[i].planenum);
 +#endif
 +		sin_dfaces[i].side = LittleShort (sin_dfaces[i].side);
 +		sin_dfaces[i].lightofs = LittleLong (sin_dfaces[i].lightofs);
 +		sin_dfaces[i].firstedge = LittleLong (sin_dfaces[i].firstedge);
 +		sin_dfaces[i].numedges = LittleShort (sin_dfaces[i].numedges);
 +	}
 +
 +//
 +// nodes
 +//
 +	for (i=0 ; i<sin_numnodes ; i++)
 +	{
 +		sin_dnodes[i].planenum = LittleLong (sin_dnodes[i].planenum);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			sin_dnodes[i].mins[j] = LittleShort (sin_dnodes[i].mins[j]);
 +			sin_dnodes[i].maxs[j] = LittleShort (sin_dnodes[i].maxs[j]);
 +		}
 +		sin_dnodes[i].children[0] = LittleLong (sin_dnodes[i].children[0]);
 +		sin_dnodes[i].children[1] = LittleLong (sin_dnodes[i].children[1]);
 +#ifdef SIN
 +		sin_dnodes[i].firstface = LittleUnsignedShort (sin_dnodes[i].firstface);
 +		sin_dnodes[i].numfaces = LittleUnsignedShort (sin_dnodes[i].numfaces);
 +#else
 +		sin_dnodes[i].firstface = LittleShort (sin_dnodes[i].firstface);
 +		sin_dnodes[i].numfaces = LittleShort (sin_dnodes[i].numfaces);
 +#endif
 +	}
 +
 +//
 +// leafs
 +//
 +	for (i=0 ; i<sin_numleafs ; i++)
 +	{
 +		sin_dleafs[i].contents = LittleLong (sin_dleafs[i].contents);
 +		sin_dleafs[i].cluster = LittleShort (sin_dleafs[i].cluster);
 +		sin_dleafs[i].area = LittleShort (sin_dleafs[i].area);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			sin_dleafs[i].mins[j] = LittleShort (sin_dleafs[i].mins[j]);
 +			sin_dleafs[i].maxs[j] = LittleShort (sin_dleafs[i].maxs[j]);
 +		}
 +#ifdef SIN
 +		sin_dleafs[i].firstleafface = LittleUnsignedShort (sin_dleafs[i].firstleafface);
 +		sin_dleafs[i].numleaffaces = LittleUnsignedShort (sin_dleafs[i].numleaffaces);
 +		sin_dleafs[i].firstleafbrush = LittleUnsignedShort (sin_dleafs[i].firstleafbrush);
 +		sin_dleafs[i].numleafbrushes = LittleUnsignedShort (sin_dleafs[i].numleafbrushes);
 +#else
 +		sin_dleafs[i].firstleafface = LittleShort (sin_dleafs[i].firstleafface);
 +		sin_dleafs[i].numleaffaces = LittleShort (sin_dleafs[i].numleaffaces);
 +		sin_dleafs[i].firstleafbrush = LittleShort (sin_dleafs[i].firstleafbrush);
 +		sin_dleafs[i].numleafbrushes = LittleShort (sin_dleafs[i].numleafbrushes);
 +#endif
 +	}
 +
 +//
 +// leaffaces
 +//
 +	for (i=0 ; i<sin_numleaffaces ; i++)
 +		sin_dleaffaces[i] = LittleShort (sin_dleaffaces[i]);
 +
 +//
 +// leafbrushes
 +//
 +	for (i=0 ; i<sin_numleafbrushes ; i++)
 +		sin_dleafbrushes[i] = LittleShort (sin_dleafbrushes[i]);
 +
 +//
 +// surfedges
 +//
 +	for (i=0 ; i<sin_numsurfedges ; i++)
 +		sin_dsurfedges[i] = LittleLong (sin_dsurfedges[i]);
 +
 +//
 +// edges
 +//
 +	for (i=0 ; i<sin_numedges ; i++)
 +	{
 +#ifdef SIN
 +		sin_dedges[i].v[0] = LittleUnsignedShort (sin_dedges[i].v[0]);
 +		sin_dedges[i].v[1] = LittleUnsignedShort (sin_dedges[i].v[1]);
 +#else
 +		sin_dedges[i].v[0] = LittleShort (sin_dedges[i].v[0]);
 +		sin_dedges[i].v[1] = LittleShort (sin_dedges[i].v[1]);
 +#endif
 +	}
 +
 +//
 +// brushes
 +//
 +	for (i=0 ; i<sin_numbrushes ; i++)
 +	{
 +		sin_dbrushes[i].firstside = LittleLong (sin_dbrushes[i].firstside);
 +		sin_dbrushes[i].numsides = LittleLong (sin_dbrushes[i].numsides);
 +		sin_dbrushes[i].contents = LittleLong (sin_dbrushes[i].contents);
 +	}
 +
 +//
 +// areas
 +//
 +	for (i=0 ; i<sin_numareas ; i++)
 +	{
 +		sin_dareas[i].numareaportals = LittleLong (sin_dareas[i].numareaportals);
 +		sin_dareas[i].firstareaportal = LittleLong (sin_dareas[i].firstareaportal);
 +	}
 +
 +//
 +// areasportals
 +//
 +	for (i=0 ; i<sin_numareaportals ; i++)
 +	{
 +		sin_dareaportals[i].portalnum = LittleLong (sin_dareaportals[i].portalnum);
 +		sin_dareaportals[i].otherarea = LittleLong (sin_dareaportals[i].otherarea);
 +	}
 +
 +//
 +// brushsides
 +//
 +	for (i=0 ; i<sin_numbrushsides ; i++)
 +	{
 +#ifdef SIN
 +		sin_dbrushsides[i].planenum = LittleUnsignedShort (sin_dbrushsides[i].planenum);
 +#else
 +		sin_dbrushsides[i].planenum = LittleShort (sin_dbrushsides[i].planenum);
 +#endif
 +		sin_dbrushsides[i].texinfo = LittleShort (sin_dbrushsides[i].texinfo);
 +#ifdef SIN
 +		sin_dbrushsides[i].lightinfo = LittleLong (sin_dbrushsides[i].lightinfo);
 +#endif
 +	}
 +
 +//
 +// visibility
 +//
 +	if (todisk)
 +		j = sin_dvis->numclusters;
 +	else
 +		j = LittleLong(sin_dvis->numclusters);
 +	sin_dvis->numclusters = LittleLong (sin_dvis->numclusters);
 +	for (i=0 ; i<j ; i++)
 +	{
 +		sin_dvis->bitofs[i][0] = LittleLong (sin_dvis->bitofs[i][0]);
 +		sin_dvis->bitofs[i][1] = LittleLong (sin_dvis->bitofs[i][1]);
 +	}
 +} //end of the function Sin_SwapBSPFile
 +
 +
 +sin_dheader_t	*header;
 +#ifdef SIN
 +int Sin_CopyLump (int lump, void *dest, int size, int maxsize)
 +{
 +	int		length, ofs;
 +
 +	length = header->lumps[lump].filelen;
 +	ofs = header->lumps[lump].fileofs;
 +	
 +	if (length % size)
 +		Error ("Sin_LoadBSPFile: odd lump size");
 +
 +   if ((length/size) > maxsize)
 +      Error ("Sin_LoadBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lump, (length/size), maxsize );
 +	
 +	memcpy (dest, (byte *)header + ofs, length);
 +
 +	return length / size;
 +}
 +#else
 +int Sin_CopyLump (int lump, void *dest, int size)
 +{
 +	int		length, ofs;
 +
 +	length = header->lumps[lump].filelen;
 +	ofs = header->lumps[lump].fileofs;
 +	
 +	if (length % size)
 +		Error ("Sin_LoadBSPFile: odd lump size");
 +	
 +	memcpy (dest, (byte *)header + ofs, length);
 +
 +	return length / size;
 +}
 +#endif
 +
 +/*
 +=============
 +Sin_LoadBSPFile
 +=============
 +*/
 +void	Sin_LoadBSPFile(char *filename, int offset, int length)
 +{
 +	int			i;
 +	
 +//
 +// load the file header
 +//
 +	LoadFile (filename, (void **)&header, offset, length);
 +
 +// swap the header
 +	for (i=0 ; i< sizeof(sin_dheader_t)/4 ; i++)
 +		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
 +
 +	if (header->ident != SIN_BSPHEADER && header->ident != SINGAME_BSPHEADER)
 +		Error ("%s is not a IBSP file", filename);
 +	if (header->version != SIN_BSPVERSION && header->version != SINGAME_BSPVERSION)
 +		Error ("%s is version %i, not %i", filename, header->version, SIN_BSPVERSION);
 +
 +#ifdef SIN
 +	sin_nummodels = Sin_CopyLump (SIN_LUMP_MODELS, sin_dmodels, sizeof(sin_dmodel_t), SIN_MAX_MAP_MODELS);
 +	sin_numvertexes = Sin_CopyLump (SIN_LUMP_VERTEXES, sin_dvertexes, sizeof(sin_dvertex_t), SIN_MAX_MAP_VERTS);
 +	sin_numplanes = Sin_CopyLump (SIN_LUMP_PLANES, sin_dplanes, sizeof(sin_dplane_t), SIN_MAX_MAP_PLANES);
 +	sin_numleafs = Sin_CopyLump (SIN_LUMP_LEAFS, sin_dleafs, sizeof(sin_dleaf_t), SIN_MAX_MAP_LEAFS);
 +	sin_numnodes = Sin_CopyLump (SIN_LUMP_NODES, sin_dnodes, sizeof(sin_dnode_t), SIN_MAX_MAP_NODES);
 +	sin_numtexinfo = Sin_CopyLump (SIN_LUMP_TEXINFO, sin_texinfo, sizeof(sin_texinfo_t), SIN_MAX_MAP_TEXINFO);
 +	sin_numfaces = Sin_CopyLump (SIN_LUMP_FACES, sin_dfaces, sizeof(sin_dface_t), SIN_MAX_MAP_FACES);
 +	sin_numleaffaces = Sin_CopyLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sizeof(sin_dleaffaces[0]), SIN_MAX_MAP_LEAFFACES);
 +	sin_numleafbrushes = Sin_CopyLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sizeof(sin_dleafbrushes[0]), SIN_MAX_MAP_LEAFBRUSHES);
 +	sin_numsurfedges = Sin_CopyLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sizeof(sin_dsurfedges[0]), SIN_MAX_MAP_SURFEDGES);
 +	sin_numedges = Sin_CopyLump (SIN_LUMP_EDGES, sin_dedges, sizeof(sin_dedge_t), SIN_MAX_MAP_EDGES);
 +	sin_numbrushes = Sin_CopyLump (SIN_LUMP_BRUSHES, sin_dbrushes, sizeof(sin_dbrush_t), SIN_MAX_MAP_BRUSHES);
 +	sin_numbrushsides = Sin_CopyLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sizeof(sin_dbrushside_t), SIN_MAX_MAP_BRUSHSIDES);
 +	sin_numareas = Sin_CopyLump (SIN_LUMP_AREAS, sin_dareas, sizeof(sin_darea_t), SIN_MAX_MAP_AREAS);
 +	sin_numareaportals = Sin_CopyLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sizeof(sin_dareaportal_t), SIN_MAX_MAP_AREAPORTALS);
 +	sin_numlightinfo = Sin_CopyLump (SIN_LUMP_LIGHTINFO, sin_lightinfo, sizeof(sin_lightvalue_t), SIN_MAX_MAP_LIGHTINFO);
 +
 +	sin_visdatasize = Sin_CopyLump (SIN_LUMP_VISIBILITY, sin_dvisdata, 1, SIN_MAX_MAP_VISIBILITY);
 +	sin_lightdatasize = Sin_CopyLump (SIN_LUMP_LIGHTING, sin_dlightdata, 1, SIN_MAX_MAP_LIGHTING);
 +	sin_entdatasize = Sin_CopyLump (SIN_LUMP_ENTITIES, sin_dentdata, 1, SIN_MAX_MAP_ENTSTRING);
 +
 +	Sin_CopyLump (SIN_LUMP_POP, sin_dpop, 1, sizeof(sin_dpop));
 +#else
 +	sin_nummodels = Sin_CopyLump (SIN_LUMP_MODELS, sin_dmodels, sizeof(sin_dmodel_t));
 +	sin_numvertexes = Sin_CopyLump (SIN_LUMP_VERTEXES, sin_dvertexes, sizeof(sin_dvertex_t));
 +	sin_numplanes = Sin_CopyLump (SIN_LUMP_PLANES, sin_dplanes, sizeof(sin_dplane_t));
 +	sin_numleafs = Sin_CopyLump (SIN_LUMP_LEAFS, sin_dleafs, sizeof(sin_dleaf_t));
 +	sin_numnodes = Sin_CopyLump (SIN_LUMP_NODES, sin_dnodes, sizeof(sin_dnode_t));
 +	sin_numtexinfo = Sin_CopyLump (SIN_LUMP_TEXINFO, sin_texinfo, sizeof(sin_texinfo_t));
 +	sin_numfaces = Sin_CopyLump (SIN_LUMP_FACES, sin_dfaces, sizeof(sin_dface_t));
 +	sin_numleaffaces = Sin_CopyLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sizeof(sin_dleaffaces[0]));
 +	sin_numleafbrushes = Sin_CopyLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sizeof(sin_dleafbrushes[0]));
 +	sin_numsurfedges = Sin_CopyLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sizeof(sin_dsurfedges[0]));
 +	sin_numedges = Sin_CopyLump (SIN_LUMP_EDGES, sin_dedges, sizeof(sin_dedge_t));
 +	sin_numbrushes = Sin_CopyLump (SIN_LUMP_BRUSHES, sin_dbrushes, sizeof(sin_dbrush_t));
 +	sin_numbrushsides = Sin_CopyLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sizeof(sin_dbrushside_t));
 +	sin_numareas = Sin_CopyLump (SIN_LUMP_AREAS, sin_dareas, sizeof(sin_darea_t));
 +	sin_numareaportals = Sin_CopyLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sizeof(sin_dareaportal_t));
 +
 +	sin_visdatasize = Sin_CopyLump (SIN_LUMP_VISIBILITY, sin_dvisdata, 1);
 +	sin_lightdatasize = Sin_CopyLump (SIN_LUMP_LIGHTING, sin_dlightdata, 1);
 +	sin_entdatasize = Sin_CopyLump (SIN_LUMP_ENTITIES, sin_dentdata, 1);
 +
 +	Sin_CopyLump (SIN_LUMP_POP, sin_dpop, 1);
 +#endif
 +
 +	FreeMemory(header);		// everything has been copied out
 +		
 +//
 +// swap everything
 +//	
 +	Sin_SwapBSPFile (false);
 +} //end of the function Sin_LoadBSPFile
 +
 +/*
 +=============
 +Sin_LoadBSPFilesTexinfo
 +
 +Only loads the sin_texinfo lump, so qdata can scan for textures
 +=============
 +*/
 +void	Sin_LoadBSPFileTexinfo (char *filename)
 +{
 +	int			i;
 +	FILE		*f;
 +	int		length, ofs;
 +
 +	header = GetMemory(sizeof(sin_dheader_t));
 +
 +	f = fopen (filename, "rb");
 +	fread (header, sizeof(sin_dheader_t), 1, f);
 +
 +// swap the header
 +	for (i=0 ; i< sizeof(sin_dheader_t)/4 ; i++)
 +		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
 +
 +	if (header->ident != SIN_BSPHEADER && header->ident != SINGAME_BSPHEADER)
 +		Error ("%s is not a IBSP file", filename);
 +	if (header->version != SIN_BSPVERSION && header->version != SINGAME_BSPVERSION)
 +		Error ("%s is version %i, not %i", filename, header->version, SIN_BSPVERSION);
 +
 +
 +	length = header->lumps[SIN_LUMP_TEXINFO].filelen;
 +	ofs = header->lumps[SIN_LUMP_TEXINFO].fileofs;
 +
 +	fseek (f, ofs, SEEK_SET);
 +	fread (sin_texinfo, length, 1, f);
 +	fclose (f);
 +
 +	sin_numtexinfo = length / sizeof(sin_texinfo_t);
 +
 +	FreeMemory(header);		// everything has been copied out
 +		
 +	Sin_SwapBSPFile (false);
 +} //end of the function Sin_LoadBSPFilesTexinfo
 +
 +
 +//============================================================================
 +
 +FILE		*wadfile;
 +sin_dheader_t	outheader;
 +
 +#ifdef SIN
 +void Sin_AddLump (int lumpnum, void *data, int len, int size, int maxsize)
 +{
 +	sin_lump_t *lump;
 +	int totallength;
 +
 +	totallength = len*size;
 +
 +	if (len > maxsize)
 +		Error ("Sin_WriteBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lumpnum, len, maxsize );
 +
 +	lump = &header->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong( ftell(wadfile) );
 +	lump->filelen = LittleLong(totallength);
 +	SafeWrite (wadfile, data, (totallength+3)&~3);
 +}
 +#else
 +void Sin_AddLump (int lumpnum, void *data, int len)
 +{
 +	sin_lump_t *lump;
 +
 +	lump = &header->lumps[lumpnum];
 +	
 +	lump->fileofs = LittleLong( ftell(wadfile) );
 +	lump->filelen = LittleLong(len);
 +	SafeWrite (wadfile, data, (len+3)&~3);
 +}
 +#endif
 +/*
 +=============
 +Sin_WriteBSPFile
 +
 +Swaps the bsp file in place, so it should not be referenced again
 +=============
 +*/
 +void	Sin_WriteBSPFile (char *filename)
 +{		
 +	header = &outheader;
 +	memset (header, 0, sizeof(sin_dheader_t));
 +	
 +	Sin_SwapBSPFile (true);
 +
 +	header->ident = LittleLong (SIN_BSPHEADER);
 +	header->version = LittleLong (SIN_BSPVERSION);
 +	
 +	wadfile = SafeOpenWrite (filename);
 +	SafeWrite (wadfile, header, sizeof(sin_dheader_t));	// overwritten later
 +
 +#ifdef SIN
 +	Sin_AddLump (SIN_LUMP_PLANES, sin_dplanes, sin_numplanes, sizeof(sin_dplane_t), SIN_MAX_MAP_PLANES);
 +	Sin_AddLump (SIN_LUMP_LEAFS, sin_dleafs, sin_numleafs, sizeof(sin_dleaf_t), SIN_MAX_MAP_LEAFS);
 +	Sin_AddLump (SIN_LUMP_VERTEXES, sin_dvertexes, sin_numvertexes, sizeof(sin_dvertex_t), SIN_MAX_MAP_VERTS);
 +	Sin_AddLump (SIN_LUMP_NODES, sin_dnodes, sin_numnodes, sizeof(sin_dnode_t), SIN_MAX_MAP_NODES);
 +	Sin_AddLump (SIN_LUMP_TEXINFO, sin_texinfo, sin_numtexinfo, sizeof(sin_texinfo_t), SIN_MAX_MAP_TEXINFO);
 +	Sin_AddLump (SIN_LUMP_FACES, sin_dfaces, sin_numfaces, sizeof(sin_dface_t), SIN_MAX_MAP_FACES);
 +	Sin_AddLump (SIN_LUMP_BRUSHES, sin_dbrushes, sin_numbrushes, sizeof(sin_dbrush_t), SIN_MAX_MAP_BRUSHES);
 +	Sin_AddLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sin_numbrushsides, sizeof(sin_dbrushside_t), SIN_MAX_MAP_BRUSHSIDES);
 +	Sin_AddLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sin_numleaffaces, sizeof(sin_dleaffaces[0]), SIN_MAX_MAP_LEAFFACES);
 +	Sin_AddLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sin_numleafbrushes, sizeof(sin_dleafbrushes[0]), SIN_MAX_MAP_LEAFBRUSHES);
 +	Sin_AddLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sin_numsurfedges, sizeof(sin_dsurfedges[0]), SIN_MAX_MAP_SURFEDGES);
 +	Sin_AddLump (SIN_LUMP_EDGES, sin_dedges, sin_numedges, sizeof(sin_dedge_t), SIN_MAX_MAP_EDGES);
 +	Sin_AddLump (SIN_LUMP_MODELS, sin_dmodels, sin_nummodels, sizeof(sin_dmodel_t), SIN_MAX_MAP_MODELS);
 +	Sin_AddLump (SIN_LUMP_AREAS, sin_dareas, sin_numareas, sizeof(sin_darea_t), SIN_MAX_MAP_AREAS);
 +	Sin_AddLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sin_numareaportals, sizeof(sin_dareaportal_t), SIN_MAX_MAP_AREAPORTALS);
 +	Sin_AddLump (SIN_LUMP_LIGHTINFO, sin_lightinfo, sin_numlightinfo, sizeof(sin_lightvalue_t), SIN_MAX_MAP_LIGHTINFO);
 +
 +	Sin_AddLump (SIN_LUMP_LIGHTING, sin_dlightdata, sin_lightdatasize, 1, SIN_MAX_MAP_LIGHTING);
 +	Sin_AddLump (SIN_LUMP_VISIBILITY, sin_dvisdata, sin_visdatasize, 1, SIN_MAX_MAP_VISIBILITY);
 +	Sin_AddLump (SIN_LUMP_ENTITIES, sin_dentdata, sin_entdatasize, 1, SIN_MAX_MAP_ENTSTRING);
 +	Sin_AddLump (SIN_LUMP_POP, sin_dpop, sizeof(sin_dpop), 1, sizeof(sin_dpop));
 +#else
 +	Sin_AddLump (SIN_LUMP_PLANES, sin_dplanes, sin_numplanes*sizeof(sin_dplane_t));
 +	Sin_AddLump (SIN_LUMP_LEAFS, sin_dleafs, sin_numleafs*sizeof(sin_dleaf_t));
 +	Sin_AddLump (SIN_LUMP_VERTEXES, sin_dvertexes, sin_numvertexes*sizeof(sin_dvertex_t));
 +	Sin_AddLump (SIN_LUMP_NODES, sin_dnodes, sin_numnodes*sizeof(sin_dnode_t));
 +	Sin_AddLump (SIN_LUMP_TEXINFO, sin_texinfo, sin_numtexinfo*sizeof(sin_texinfo_t));
 +	Sin_AddLump (SIN_LUMP_FACES, sin_dfaces, sin_numfaces*sizeof(sin_dface_t));
 +	Sin_AddLump (SIN_LUMP_BRUSHES, sin_dbrushes, sin_numbrushes*sizeof(sin_dbrush_t));
 +	Sin_AddLump (SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sin_numbrushsides*sizeof(sin_dbrushside_t));
 +	Sin_AddLump (SIN_LUMP_LEAFFACES, sin_dleaffaces, sin_numleaffaces*sizeof(sin_dleaffaces[0]));
 +	Sin_AddLump (SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sin_numleafbrushes*sizeof(sin_dleafbrushes[0]));
 +	Sin_AddLump (SIN_LUMP_SURFEDGES, sin_dsurfedges, sin_numsurfedges*sizeof(sin_dsurfedges[0]));
 +	Sin_AddLump (SIN_LUMP_EDGES, sin_dedges, sin_numedges*sizeof(sin_dedge_t));
 +	Sin_AddLump (SIN_LUMP_MODELS, sin_dmodels, sin_nummodels*sizeof(sin_dmodel_t));
 +	Sin_AddLump (SIN_LUMP_AREAS, sin_dareas, sin_numareas*sizeof(sin_darea_t));
 +	Sin_AddLump (SIN_LUMP_AREAPORTALS, sin_dareaportals, sin_numareaportals*sizeof(sin_dareaportal_t));
 +
 +	Sin_AddLump (SIN_LUMP_LIGHTING, sin_dlightdata, sin_lightdatasize);
 +	Sin_AddLump (SIN_LUMP_VISIBILITY, sin_dvisdata, sin_visdatasize);
 +	Sin_AddLump (SIN_LUMP_ENTITIES, sin_dentdata, sin_entdatasize);
 +	Sin_AddLump (SIN_LUMP_POP, sin_dpop, sizeof(sin_dpop));
 +#endif
 +	
 +	fseek (wadfile, 0, SEEK_SET);
 +	SafeWrite (wadfile, header, sizeof(sin_dheader_t));
 +	fclose (wadfile);	
 +}
 +
 +//============================================================================
 +
 +
 +//============================================
 +
 +/*
 +================
 +ParseEntities
 +
 +Parses the sin_dentdata string into entities
 +================
 +*/
 +void Sin_ParseEntities (void)
 +{
 +	script_t *script;
 +
 +	num_entities = 0;
 +	script = LoadScriptMemory(sin_dentdata, sin_entdatasize, "*sin bsp file");
 +	SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES |
 +									SCFL_NOSTRINGESCAPECHARS);
 +
 +	while(ParseEntity(script))
 +	{
 +	} //end while
 +
 +	FreeScript(script);
 +} //end of the function Sin_ParseEntities
 +
 +
 +/*
 +================
 +UnparseEntities
 +
 +Generates the sin_dentdata string from all the entities
 +================
 +*/
 +void Sin_UnparseEntities (void)
 +{
 +	char	*buf, *end;
 +	epair_t	*ep;
 +	char	line[2048];
 +	int		i;
 +	char	key[1024], value[1024];
 +
 +	buf = sin_dentdata;
 +	end = buf;
 +	*end = 0;
 +	
 +	for (i=0 ; i<num_entities ; i++)
 +	{
 +		ep = entities[i].epairs;
 +		if (!ep)
 +			continue;	// ent got removed
 +		
 +		strcat (end,"{\n");
 +		end += 2;
 +				
 +		for (ep = entities[i].epairs ; ep ; ep=ep->next)
 +		{
 +			strcpy (key, ep->key);
 +			StripTrailing (key);
 +			strcpy (value, ep->value);
 +			StripTrailing (value);
 +				
 +			sprintf (line, "\"%s\" \"%s\"\n", key, value);
 +			strcat (end, line);
 +			end += strlen(line);
 +		}
 +		strcat (end,"}\n");
 +		end += 2;
 +
 +		if (end > buf + SIN_MAX_MAP_ENTSTRING)
 +			Error ("Entity text too long");
 +	}
 +	sin_entdatasize = end - buf + 1;
 +} //end of the function Sin_UnparseEntities
 +
 +#ifdef SIN
 +void  FreeValueKeys(entity_t *ent)
 +{
 +	epair_t	*ep,*next;
 +
 +	for (ep=ent->epairs ; ep ; ep=next)
 +	{
 +		next = ep->next;
 +		FreeMemory(ep->value);
 +		FreeMemory(ep->key);
 +		FreeMemory(ep);
 +	}
 +	ent->epairs = NULL;
 +}
 +#endif
 +
 +/*
 +=============
 +Sin_PrintBSPFileSizes
 +
 +Dumps info about current file
 +=============
 +*/
 +void Sin_PrintBSPFileSizes (void)
 +{
 +	if (!num_entities)
 +		Sin_ParseEntities ();
 +
 +	Log_Print("%6i models       %7i\n"
 +		,sin_nummodels, (int)(sin_nummodels*sizeof(sin_dmodel_t)));
 +	Log_Print("%6i brushes      %7i\n"
 +		,sin_numbrushes, (int)(sin_numbrushes*sizeof(sin_dbrush_t)));
 +	Log_Print("%6i brushsides   %7i\n"
 +		,sin_numbrushsides, (int)(sin_numbrushsides*sizeof(sin_dbrushside_t)));
 +	Log_Print("%6i planes       %7i\n"
 +		,sin_numplanes, (int)(sin_numplanes*sizeof(sin_dplane_t)));
 +	Log_Print("%6i texinfo      %7i\n"
 +		,sin_numtexinfo, (int)(sin_numtexinfo*sizeof(sin_texinfo_t)));
 +#ifdef SIN
 +	Log_Print("%6i lightinfo    %7i\n"
 +		,sin_numlightinfo, (int)(sin_numlightinfo*sizeof(sin_lightvalue_t)));
 +#endif
 +	Log_Print("%6i entdata      %7i\n", num_entities, sin_entdatasize);
 +
 +	Log_Print("\n");
 +
 +	Log_Print("%6i vertexes     %7i\n"
 +		,sin_numvertexes, (int)(sin_numvertexes*sizeof(sin_dvertex_t)));
 +	Log_Print("%6i nodes        %7i\n"
 +		,sin_numnodes, (int)(sin_numnodes*sizeof(sin_dnode_t)));
 +	Log_Print("%6i faces        %7i\n"
 +		,sin_numfaces, (int)(sin_numfaces*sizeof(sin_dface_t)));
 +	Log_Print("%6i leafs        %7i\n"
 +		,sin_numleafs, (int)(sin_numleafs*sizeof(sin_dleaf_t)));
 +	Log_Print("%6i leaffaces    %7i\n"
 +		,sin_numleaffaces, (int)(sin_numleaffaces*sizeof(sin_dleaffaces[0])));
 +	Log_Print("%6i leafbrushes  %7i\n"
 +		,sin_numleafbrushes, (int)(sin_numleafbrushes*sizeof(sin_dleafbrushes[0])));
 +	Log_Print("%6i surfedges    %7i\n"
 +		,sin_numsurfedges, (int)(sin_numsurfedges*sizeof(sin_dsurfedges[0])));
 +	Log_Print("%6i edges        %7i\n"
 +		,sin_numedges, (int)(sin_numedges*sizeof(sin_dedge_t)));
 +	Log_Print("       lightdata    %7i\n", sin_lightdatasize);
 +	Log_Print("       visdata      %7i\n", sin_visdatasize);
 +}
 diff --git a/code/bspc/l_bsp_sin.h b/code/bspc/l_bsp_sin.h new file mode 100755 index 0000000..93cb69f --- /dev/null +++ b/code/bspc/l_bsp_sin.h @@ -0,0 +1,106 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "sinfiles.h"
 +
 +#define SINGAME_BSPHEADER		(('P'<<24)+('S'<<16)+('B'<<8)+'R')	//RBSP
 +#define SINGAME_BSPVERSION		1
 +
 +#define SIN_BSPHEADER			(('P'<<24)+('S'<<16)+('B'<<8)+'I')	//IBSP
 +#define SIN_BSPVERSION	41
 +
 +
 +extern	int					sin_nummodels;
 +extern	sin_dmodel_t		*sin_dmodels;//[MAX_MAP_MODELS];
 +
 +extern	int					sin_visdatasize;
 +extern	byte				*sin_dvisdata;//[MAX_MAP_VISIBILITY];
 +extern	sin_dvis_t			*sin_dvis;// = (dvis_t *)sin_sin_dvisdata;
 +
 +extern	int					sin_lightdatasize;
 +extern	byte				*sin_dlightdata;//[MAX_MAP_LIGHTING];
 +
 +extern	int					sin_entdatasize;
 +extern	char				*sin_dentdata;//[MAX_MAP_ENTSTRING];
 +
 +extern	int					sin_numleafs;
 +extern	sin_dleaf_t			*sin_dleafs;//[MAX_MAP_LEAFS];
 +
 +extern	int					sin_numplanes;
 +extern	sin_dplane_t		*sin_dplanes;//[MAX_MAP_PLANES];
 +
 +extern	int					sin_numvertexes;
 +extern	sin_dvertex_t		*sin_dvertexes;//[MAX_MAP_VERTS];
 +
 +extern	int					sin_numnodes;
 +extern	sin_dnode_t			*sin_dnodes;//[MAX_MAP_NODES];
 +
 +extern	int					sin_numtexinfo;
 +extern	sin_texinfo_t		*sin_texinfo;//[MAX_MAP_sin_texinfo];
 +
 +extern	int					sin_numfaces;
 +extern	sin_dface_t			*sin_dfaces;//[MAX_MAP_FACES];
 +
 +extern	int					sin_numedges;
 +extern	sin_dedge_t			*sin_dedges;//[MAX_MAP_EDGES];
 +
 +extern	int					sin_numleaffaces;
 +extern	unsigned short		*sin_dleaffaces;//[MAX_MAP_LEAFFACES];
 +
 +extern	int					sin_numleafbrushes;
 +extern	unsigned short		*sin_dleafbrushes;//[MAX_MAP_LEAFBRUSHES];
 +
 +extern	int					sin_numsurfedges;
 +extern	int					*sin_dsurfedges;//[MAX_MAP_SURFEDGES];
 +
 +extern	int					sin_numbrushes;
 +extern	sin_dbrush_t		*sin_dbrushes;//[MAX_MAP_BRUSHES];
 +
 +extern	int					sin_numbrushsides;
 +extern	sin_dbrushside_t	*sin_dbrushsides;//[MAX_MAP_BRUSHSIDES];
 +
 +extern	int					sin_numareas;
 +extern	sin_darea_t			*sin_dareas;//[MAX_MAP_AREAS];
 +
 +extern	int					sin_numareaportals;
 +extern	sin_dareaportal_t	*sin_dareaportals;//[MAX_MAP_AREAPORTALS];
 +
 +extern	int					sin_numlightinfo;
 +extern	sin_lightvalue_t	*sin_lightinfo;//[MAX_MAP_LIGHTINFO];
 +
 +extern	byte				sin_dpop[256];
 +
 +extern	char				sin_dbrushsidetextured[SIN_MAX_MAP_BRUSHSIDES];
 +
 +void Sin_AllocMaxBSP(void);
 +void Sin_FreeMaxBSP(void);
 +
 +void Sin_DecompressVis(byte *in, byte *decompressed);
 +int Sin_CompressVis(byte *vis, byte *dest);
 +
 +void Sin_LoadBSPFile (char *filename, int offset, int length);
 +void Sin_LoadBSPFileTexinfo (char *filename);	// just for qdata
 +void Sin_WriteBSPFile (char *filename);
 +void Sin_PrintBSPFileSizes (void);
 +void Sin_ParseEntities(void);
 +void Sin_UnparseEntities(void);
 +
 diff --git a/code/bspc/l_cmd.c b/code/bspc/l_cmd.c new file mode 100755 index 0000000..35fef7b --- /dev/null +++ b/code/bspc/l_cmd.c @@ -0,0 +1,1230 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +// cmdlib.c
 +
 +#include "l_cmd.h"
 +#include "l_log.h"
 +#include "l_mem.h"
 +#include <sys/types.h>
 +#include <sys/stat.h>
 +
 +#ifndef SIN
 +#define SIN
 +#endif //SIN
 +
 +#if defined(WIN32) || defined(_WIN32)
 +#include <direct.h>
 +#else
 +#include <unistd.h>
 +#endif
 +
 +#ifdef NeXT
 +#include <libc.h>
 +#endif
 +
 +#define	BASEDIRNAME	"quake2"
 +#define PATHSEPERATOR   '/'
 +
 +// set these before calling CheckParm
 +int myargc;
 +char **myargv;
 +
 +char		com_token[1024];
 +qboolean	com_eof;
 +
 +qboolean		archive;
 +char			archivedir[1024];
 +
 +
 +/*
 +===================
 +ExpandWildcards
 +
 +Mimic unix command line expansion
 +===================
 +*/
 +#define	MAX_EX_ARGC	1024
 +int		ex_argc;
 +char	*ex_argv[MAX_EX_ARGC];
 +#ifdef _WIN32
 +#include "io.h"
 +void ExpandWildcards (int *argc, char ***argv)
 +{
 +	struct _finddata_t fileinfo;
 +	int		handle;
 +	int		i;
 +	char	filename[1024];
 +	char	filebase[1024];
 +	char	*path;
 +
 +	ex_argc = 0;
 +	for (i=0 ; i<*argc ; i++)
 +	{
 +		path = (*argv)[i];
 +		if ( path[0] == '-'
 +			|| ( !strstr(path, "*") && !strstr(path, "?") ) )
 +		{
 +			ex_argv[ex_argc++] = path;
 +			continue;
 +		}
 +
 +		handle = _findfirst (path, &fileinfo);
 +		if (handle == -1)
 +			return;
 +
 +		ExtractFilePath (path, filebase);
 +
 +		do
 +		{
 +			sprintf (filename, "%s%s", filebase, fileinfo.name);
 +			ex_argv[ex_argc++] = copystring (filename);
 +		} while (_findnext( handle, &fileinfo ) != -1);
 +
 +		_findclose (handle);
 +	}
 +
 +	*argc = ex_argc;
 +	*argv = ex_argv;
 +}
 +#else
 +void ExpandWildcards (int *argc, char ***argv)
 +{
 +}
 +#endif
 +
 +#ifdef WINBSPC
 +
 +#include <windows.h>
 +
 +HWND program_hwnd;
 +
 +void SetProgramHandle(HWND hwnd)
 +{
 +	program_hwnd = hwnd;
 +} //end of the function SetProgramHandle
 +
 +/*
 +=================
 +Error
 +
 +For abnormal program terminations in windowed apps
 +=================
 +*/
 +void Error (char *error, ...)
 +{
 +	va_list argptr;
 +	char text[1024];
 +	char text2[1024];
 +	int err;
 +
 +	err = GetLastError ();
 +
 +	va_start(argptr, error);
 +	vsprintf(text, error, argptr);
 +	va_end(argptr);
 +
 +	sprintf(text2, "%s\nGetLastError() = %i", text, err);
 +   MessageBox(program_hwnd, text2, "Error", 0 /* MB_OK */ );
 +
 +	Log_Write(text);
 +	Log_Close();
 +
 +	exit(1);
 +} //end of the function Error
 +
 +void Warning(char *szFormat, ...)
 +{
 +	char szBuffer[256];
 +	va_list argptr;
 +
 +	va_start (argptr, szFormat);
 +	vsprintf(szBuffer, szFormat, argptr);
 +	va_end (argptr);
 +
 +	MessageBox(program_hwnd, szBuffer, "Warning", MB_OK);
 +
 +	Log_Write(szBuffer);
 +} //end of the function Warning
 +
 +
 +#else
 +/*
 +=================
 +Error
 +
 +For abnormal program terminations in console apps
 +=================
 +*/
 +void Error (char *error, ...)
 +{
 +	va_list argptr;
 +	char	text[1024];
 +
 +	va_start(argptr, error);
 +	vsprintf(text, error, argptr);
 +	va_end(argptr);
 +	printf("ERROR: %s\n", text);
 +
 +	Log_Write(text);
 +	Log_Close();
 +
 +	exit (1);
 +} //end of the function Error
 +
 +void Warning(char *warning, ...)
 +{
 +	va_list argptr;
 +	char text[1024];
 +
 +	va_start(argptr, warning);
 +	vsprintf(text, warning, argptr);
 +	va_end(argptr);
 +	printf("WARNING: %s\n", text);
 +
 +	Log_Write(text);
 +} //end of the function Warning
 +
 +#endif
 +
 +//only printf if in verbose mode
 +qboolean verbose = true;
 +
 +void qprintf(char *format, ...)
 +{
 +	va_list argptr;
 +#ifdef WINBSPC
 +	char buf[2048];
 +#endif //WINBSPC
 +
 +	if (!verbose)
 +		return;
 +
 +	va_start(argptr,format);
 +#ifdef WINBSPC
 +	vsprintf(buf, format, argptr);
 +	WinBSPCPrint(buf);
 +#else
 +	vprintf(format, argptr);
 +#endif //WINBSPC
 +	va_end(argptr);
 +} //end of the function qprintf
 +
 +void Com_Error(int level, char *error, ...)
 +{
 +	va_list argptr;
 +	char text[1024];
 +
 +	va_start(argptr, error);
 +	vsprintf(text, error, argptr);
 +	va_end(argptr);
 +	Error(text);
 +} //end of the funcion Com_Error
 +
 +void Com_Printf( const char *fmt, ... )
 +{
 +	va_list argptr;
 +	char text[1024];
 +
 +	va_start(argptr, fmt);
 +	vsprintf(text, fmt, argptr);
 +	va_end(argptr);
 +	Log_Print(text);
 +} //end of the funcion Com_Printf
 +
 +/*
 +
 +qdir will hold the path up to the quake directory, including the slash
 +
 +  f:\quake\
 +  /raid/quake/
 +
 +gamedir will hold qdir + the game directory (id1, id2, etc)
 +
 +  */
 +
 +char		qdir[1024];
 +char		gamedir[1024];
 +
 +void SetQdirFromPath (char *path)
 +{
 +	char	temp[1024];
 +	char	*c;
 +	int		len;
 +
 +	if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':'))
 +	{	// path is partial
 +		Q_getwd (temp);
 +		strcat (temp, path);
 +		path = temp;
 +	}
 +
 +	// search for "quake2" in path
 +
 +	len = strlen(BASEDIRNAME);
 +	for (c=path+strlen(path)-1 ; c != path ; c--)
 +		if (!Q_strncasecmp (c, BASEDIRNAME, len))
 +		{
 +			strncpy (qdir, path, c+len+1-path);
 +			qprintf ("qdir: %s\n", qdir);
 +			c += len+1;
 +			while (*c)
 +			{
 +				if (*c == '/' || *c == '\\')
 +				{
 +					strncpy (gamedir, path, c+1-path);
 +					qprintf ("gamedir: %s\n", gamedir);
 +					return;
 +				}
 +				c++;
 +			}
 +			Error ("No gamedir in %s", path);
 +			return;
 +		}
 +	Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path);
 +}
 +
 +char *ExpandArg (char *path)
 +{
 +	static char full[1024];
 +
 +	if (path[0] != '/' && path[0] != '\\' && path[1] != ':')
 +	{
 +		Q_getwd (full);
 +		strcat (full, path);
 +	}
 +	else
 +		strcpy (full, path);
 +	return full;
 +}
 +
 +char *ExpandPath (char *path)
 +{
 +	static char full[1024];
 +	if (!qdir)
 +		Error ("ExpandPath called without qdir set");
 +	if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
 +		return path;
 +	sprintf (full, "%s%s", qdir, path);
 +	return full;
 +}
 +
 +char *ExpandPathAndArchive (char *path)
 +{
 +	char	*expanded;
 +	char	archivename[1024];
 +
 +	expanded = ExpandPath (path);
 +
 +	if (archive)
 +	{
 +		sprintf (archivename, "%s/%s", archivedir, path);
 +		QCopyFile (expanded, archivename);
 +	}
 +	return expanded;
 +}
 +
 +
 +char *copystring(char *s)
 +{
 +	char	*b;
 +	b = GetMemory(strlen(s)+1);
 +	strcpy (b, s);
 +	return b;
 +}
 +
 +
 +
 +/*
 +================
 +I_FloatTime
 +================
 +*/
 +double I_FloatTime (void)
 +{
 +	time_t	t;
 +	
 +	time (&t);
 +	
 +	return t;
 +#if 0
 +// more precise, less portable
 +	struct timeval tp;
 +	struct timezone tzp;
 +	static int		secbase;
 +
 +	gettimeofday(&tp, &tzp);
 +	
 +	if (!secbase)
 +	{
 +		secbase = tp.tv_sec;
 +		return tp.tv_usec/1000000.0;
 +	}
 +	
 +	return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
 +#endif
 +}
 +
 +void Q_getwd (char *out)
 +{
 +#if defined(WIN32) || defined(_WIN32)
 +   getcwd (out, 256);
 +   strcat (out, "\\");
 +#else
 +   getwd(out);
 +   strcat(out, "/");
 +#endif
 +}
 +
 +
 +void Q_mkdir (char *path)
 +{
 +#ifdef WIN32
 +	if (_mkdir (path) != -1)
 +		return;
 +#else
 +	if (mkdir (path, 0777) != -1)
 +		return;
 +#endif
 +	if (errno != EEXIST)
 +		Error ("mkdir %s: %s",path, strerror(errno));
 +}
 +
 +/*
 +============
 +FileTime
 +
 +returns -1 if not present
 +============
 +*/
 +int	FileTime (char *path)
 +{
 +	struct	stat	buf;
 +	
 +	if (stat (path,&buf) == -1)
 +		return -1;
 +	
 +	return buf.st_mtime;
 +}
 +
 +
 +
 +/*
 +==============
 +COM_Parse
 +
 +Parse a token out of a string
 +==============
 +*/
 +char *COM_Parse (char *data)
 +{
 +	int		c;
 +	int		len;
 +	
 +	len = 0;
 +	com_token[0] = 0;
 +	
 +	if (!data)
 +		return NULL;
 +		
 +// skip whitespace
 +skipwhite:
 +	while ( (c = *data) <= ' ')
 +	{
 +		if (c == 0)
 +		{
 +			com_eof = true;
 +			return NULL;			// end of file;
 +		}
 +		data++;
 +	}
 +	
 +// skip // comments
 +	if (c=='/' && data[1] == '/')
 +	{
 +		while (*data && *data != '\n')
 +			data++;
 +		goto skipwhite;
 +	}
 +	
 +
 +// handle quoted strings specially
 +	if (c == '\"')
 +	{
 +		data++;
 +		do
 +		{
 +			c = *data++;
 +			if (c=='\"')
 +			{
 +				com_token[len] = 0;
 +				return data;
 +			}
 +			com_token[len] = c;
 +			len++;
 +		} while (1);
 +	}
 +
 +// parse single characters
 +	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
 +	{
 +		com_token[len] = c;
 +		len++;
 +		com_token[len] = 0;
 +		return data+1;
 +	}
 +
 +// parse a regular word
 +	do
 +	{
 +		com_token[len] = c;
 +		data++;
 +		len++;
 +		c = *data;
 +	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
 +			break;
 +	} while (c>32);
 +	
 +	com_token[len] = 0;
 +	return data;
 +}
 +
 +
 +int Q_strncasecmp (char *s1, char *s2, int n)
 +{
 +	int		c1, c2;
 +	
 +	do
 +	{
 +		c1 = *s1++;
 +		c2 = *s2++;
 +
 +		if (!n--)
 +			return 0;		// strings are equal until end point
 +		
 +		if (c1 != c2)
 +		{
 +			if (c1 >= 'a' && c1 <= 'z')
 +				c1 -= ('a' - 'A');
 +			if (c2 >= 'a' && c2 <= 'z')
 +				c2 -= ('a' - 'A');
 +			if (c1 != c2)
 +				return -1;		// strings not equal
 +		}
 +	} while (c1);
 +	
 +	return 0;		// strings are equal
 +}
 +
 +int Q_strcasecmp (char *s1, char *s2)
 +{
 +	return Q_strncasecmp (s1, s2, 99999);
 +}
 +
 +int Q_stricmp (char *s1, char *s2)
 +{
 +	return Q_strncasecmp (s1, s2, 99999);
 +}
 +
 +void Q_strncpyz( char *dest, const char *src, int destsize ) {
 +	strncpy( dest, src, destsize-1 );
 +    dest[destsize-1] = 0;
 +}
 +
 +char *strupr (char *start)
 +{
 +	char	*in;
 +	in = start;
 +	while (*in)
 +	{
 +		*in = toupper(*in);
 +		in++;
 +	}
 +	return start;
 +}
 +
 +char *strlower (char *start)
 +{
 +	char	*in;
 +	in = start;
 +	while (*in)
 +	{
 +		*in = tolower(*in); 
 +		in++;
 +	}
 +	return start;
 +}
 +
 +
 +/*
 +=============================================================================
 +
 +						MISC FUNCTIONS
 +
 +=============================================================================
 +*/
 +
 +
 +/*
 +=================
 +CheckParm
 +
 +Checks for the given parameter in the program's command line arguments
 +Returns the argument number (1 to argc-1) or 0 if not present
 +=================
 +*/
 +int CheckParm (char *check)
 +{
 +	int             i;
 +
 +	for (i = 1;i<myargc;i++)
 +	{
 +		if ( !Q_strcasecmp(check, myargv[i]) )
 +			return i;
 +	}
 +
 +	return 0;
 +}
 +
 +
 +
 +/*
 +================
 +Q_filelength
 +================
 +*/
 +int Q_filelength (FILE *f)
 +{
 +	int		pos;
 +	int		end;
 +
 +	pos = ftell (f);
 +	fseek (f, 0, SEEK_END);
 +	end = ftell (f);
 +	fseek (f, pos, SEEK_SET);
 +
 +	return end;
 +}
 +
 +
 +FILE *SafeOpenWrite (char *filename)
 +{
 +	FILE	*f;
 +
 +	f = fopen(filename, "wb");
 +
 +	if (!f)
 +		Error ("Error opening %s: %s",filename,strerror(errno));
 +
 +	return f;
 +}
 +
 +FILE *SafeOpenRead (char *filename)
 +{
 +	FILE	*f;
 +
 +	f = fopen(filename, "rb");
 +
 +	if (!f)
 +		Error ("Error opening %s: %s",filename,strerror(errno));
 +
 +	return f;
 +}
 +
 +
 +void SafeRead (FILE *f, void *buffer, int count)
 +{
 +	if ( fread (buffer, 1, count, f) != (size_t)count)
 +		Error ("File read failure");
 +}
 +
 +
 +void SafeWrite (FILE *f, void *buffer, int count)
 +{
 +	if (fwrite (buffer, 1, count, f) != (size_t)count)
 +		Error ("File write failure");
 +}
 +
 +
 +/*
 +==============
 +FileExists
 +==============
 +*/
 +qboolean	FileExists (char *filename)
 +{
 +	FILE	*f;
 +
 +	f = fopen (filename, "r");
 +	if (!f)
 +		return false;
 +	fclose (f);
 +	return true;
 +}
 +
 +/*
 +==============
 +LoadFile
 +==============
 +*/
 +int    LoadFile (char *filename, void **bufferptr, int offset, int length)
 +{
 +	FILE	*f;
 +	void    *buffer;
 +
 +	f = SafeOpenRead(filename);
 +	fseek(f, offset, SEEK_SET);
 +	if (!length) length = Q_filelength(f);
 +	buffer = GetMemory(length+1);
 +	((char *)buffer)[length] = 0;
 +	SafeRead(f, buffer, length);
 +	fclose(f);
 +
 +	*bufferptr = buffer;
 +	return length;
 +}
 +
 +
 +/*
 +==============
 +TryLoadFile
 +
 +Allows failure
 +==============
 +*/
 +int    TryLoadFile (char *filename, void **bufferptr)
 +{
 +	FILE	*f;
 +	int    length;
 +	void    *buffer;
 +
 +	*bufferptr = NULL;
 +
 +	f = fopen (filename, "rb");
 +	if (!f)
 +		return -1;
 +	length = Q_filelength (f);
 +	buffer = GetMemory(length+1);
 +	((char *)buffer)[length] = 0;
 +	SafeRead (f, buffer, length);
 +	fclose (f);
 +
 +	*bufferptr = buffer;
 +	return length;
 +}
 +
 +
 +/*
 +==============
 +SaveFile
 +==============
 +*/
 +void    SaveFile (char *filename, void *buffer, int count)
 +{
 +	FILE	*f;
 +
 +	f = SafeOpenWrite (filename);
 +	SafeWrite (f, buffer, count);
 +	fclose (f);
 +}
 +
 +
 +
 +void DefaultExtension (char *path, char *extension)
 +{
 +	char    *src;
 +//
 +// if path doesnt have a .EXT, append extension
 +// (extension should include the .)
 +//
 +	src = path + strlen(path) - 1;
 +
 +	while (*src != PATHSEPERATOR && src != path)
 +	{
 +		if (*src == '.')
 +			return;                 // it has an extension
 +		src--;
 +	}
 +
 +	strcat (path, extension);
 +}
 +
 +
 +void DefaultPath (char *path, char *basepath)
 +{
 +	char    temp[128];
 +
 +	if (path[0] == PATHSEPERATOR)
 +		return;                   // absolute path location
 +	strcpy (temp,path);
 +	strcpy (path,basepath);
 +	strcat (path,temp);
 +}
 +
 +
 +void    StripFilename (char *path)
 +{
 +	int             length;
 +
 +	length = strlen(path)-1;
 +	while (length > 0 && path[length] != PATHSEPERATOR)
 +		length--;
 +	path[length] = 0;
 +}
 +
 +void    StripExtension (char *path)
 +{
 +	int             length;
 +
 +	length = strlen(path)-1;
 +	while (length > 0 && path[length] != '.')
 +	{
 +		length--;
 +		if (path[length] == '/')
 +			return;		// no extension
 +	}
 +	if (length)
 +		path[length] = 0;
 +}
 +
 +
 +/*
 +====================
 +Extract file parts
 +====================
 +*/
 +// FIXME: should include the slash, otherwise
 +// backing to an empty path will be wrong when appending a slash
 +void ExtractFilePath (char *path, char *dest)
 +{
 +	char    *src;
 +
 +	src = path + strlen(path) - 1;
 +
 +//
 +// back up until a \ or the start
 +//
 +	while (src != path && *(src-1) != '\\' && *(src-1) != '/')
 +		src--;
 +
 +	memcpy (dest, path, src-path);
 +	dest[src-path] = 0;
 +}
 +
 +void ExtractFileBase (char *path, char *dest)
 +{
 +	char    *src;
 +
 +	src = path + strlen(path) - 1;
 +
 +//
 +// back up until a \ or the start
 +//
 +	while (src != path && *(src-1) != '\\' && *(src-1) != '/')
 +		src--;
 +
 +	while (*src && *src != '.')
 +	{
 +		*dest++ = *src++;
 +	}
 +	*dest = 0;
 +}
 +
 +void ExtractFileExtension (char *path, char *dest)
 +{
 +	char    *src;
 +
 +	src = path + strlen(path) - 1;
 +
 +//
 +// back up until a . or the start
 +//
 +	while (src != path && *(src-1) != '.')
 +		src--;
 +	if (src == path)
 +	{
 +		*dest = 0;	// no extension
 +		return;
 +	}
 +
 +	strcpy (dest,src);
 +}
 +
 +
 +/*
 +==============
 +ParseNum / ParseHex
 +==============
 +*/
 +int ParseHex (char *hex)
 +{
 +	char    *str;
 +	int    num;
 +
 +	num = 0;
 +	str = hex;
 +
 +	while (*str)
 +	{
 +		num <<= 4;
 +		if (*str >= '0' && *str <= '9')
 +			num += *str-'0';
 +		else if (*str >= 'a' && *str <= 'f')
 +			num += 10 + *str-'a';
 +		else if (*str >= 'A' && *str <= 'F')
 +			num += 10 + *str-'A';
 +		else
 +			Error ("Bad hex number: %s",hex);
 +		str++;
 +	}
 +
 +	return num;
 +}
 +
 +
 +int ParseNum (char *str)
 +{
 +	if (str[0] == '$')
 +		return ParseHex (str+1);
 +	if (str[0] == '0' && str[1] == 'x')
 +		return ParseHex (str+2);
 +	return atol (str);
 +}
 +
 +
 +
 +/*
 +============================================================================
 +
 +					BYTE ORDER FUNCTIONS
 +
 +============================================================================
 +*/
 +
 +#ifdef _SGI_SOURCE
 +#define	__BIG_ENDIAN__
 +#endif
 +
 +#ifdef __BIG_ENDIAN__
 +
 +short   LittleShort (short l)
 +{
 +	byte    b1,b2;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +
 +	return (b1<<8) + b2;
 +}
 +
 +short   BigShort (short l)
 +{
 +	return l;
 +}
 +
 +
 +int    LittleLong (int l)
 +{
 +	byte    b1,b2,b3,b4;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +	b3 = (l>>16)&255;
 +	b4 = (l>>24)&255;
 +
 +	return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
 +}
 +
 +int    BigLong (int l)
 +{
 +	return l;
 +}
 +
 +
 +float	LittleFloat (float l)
 +{
 +	union {byte b[4]; float f;} in, out;
 +	
 +	in.f = l;
 +	out.b[0] = in.b[3];
 +	out.b[1] = in.b[2];
 +	out.b[2] = in.b[1];
 +	out.b[3] = in.b[0];
 +	
 +	return out.f;
 +}
 +
 +float	BigFloat (float l)
 +{
 +	return l;
 +}
 +
 +#ifdef SIN
 +unsigned short   LittleUnsignedShort (unsigned short l)
 +{
 +	byte    b1,b2;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +
 +	return (b1<<8) + b2;
 +}
 +
 +unsigned short   BigUnsignedShort (unsigned short l)
 +{
 +	return l;
 +}
 +
 +unsigned    LittleUnsigned (unsigned l)
 +{
 +	byte    b1,b2,b3,b4;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +	b3 = (l>>16)&255;
 +	b4 = (l>>24)&255;
 +
 +	return ((unsigned)b1<<24) + ((unsigned)b2<<16) + ((unsigned)b3<<8) + b4;
 +}
 +
 +unsigned    BigUnsigned (unsigned l)
 +{
 +	return l;
 +}
 +#endif
 +
 +
 +#else
 +
 +
 +short   BigShort (short l)
 +{
 +	byte    b1,b2;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +
 +	return (b1<<8) + b2;
 +}
 +
 +short   LittleShort (short l)
 +{
 +	return l;
 +}
 +
 +
 +int    BigLong (int l)
 +{
 +	byte    b1,b2,b3,b4;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +	b3 = (l>>16)&255;
 +	b4 = (l>>24)&255;
 +
 +	return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
 +}
 +
 +int    LittleLong (int l)
 +{
 +	return l;
 +}
 +
 +float	BigFloat (float l)
 +{
 +	union {byte b[4]; float f;} in, out;
 +	
 +	in.f = l;
 +	out.b[0] = in.b[3];
 +	out.b[1] = in.b[2];
 +	out.b[2] = in.b[1];
 +	out.b[3] = in.b[0];
 +	
 +	return out.f;
 +}
 +
 +float	LittleFloat (float l)
 +{
 +	return l;
 +}
 +
 +#ifdef SIN
 +unsigned short   BigUnsignedShort (unsigned short l)
 +{
 +	byte    b1,b2;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +
 +	return (b1<<8) + b2;
 +}
 +
 +unsigned short   LittleUnsignedShort (unsigned short l)
 +{
 +	return l;
 +}
 +
 +
 +unsigned    BigUnsigned (unsigned l)
 +{
 +	byte    b1,b2,b3,b4;
 +
 +	b1 = l&255;
 +	b2 = (l>>8)&255;
 +	b3 = (l>>16)&255;
 +	b4 = (l>>24)&255;
 +
 +	return ((unsigned)b1<<24) + ((unsigned)b2<<16) + ((unsigned)b3<<8) + b4;
 +}
 +
 +unsigned    LittleUnsigned (unsigned l)
 +{
 +	return l;
 +}
 +#endif
 +
 +
 +#endif
 +
 +
 +//=======================================================
 +
 +
 +// FIXME: byte swap?
 +
 +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
 +// and the initial and final xor values shown below...  in other words, the
 +// CCITT standard CRC used by XMODEM
 +
 +#define CRC_INIT_VALUE	0xffff
 +#define CRC_XOR_VALUE	0x0000
 +
 +static unsigned short crctable[256] =
 +{
 +	0x0000,	0x1021,	0x2042,	0x3063,	0x4084,	0x50a5,	0x60c6,	0x70e7,
 +	0x8108,	0x9129,	0xa14a,	0xb16b,	0xc18c,	0xd1ad,	0xe1ce,	0xf1ef,
 +	0x1231,	0x0210,	0x3273,	0x2252,	0x52b5,	0x4294,	0x72f7,	0x62d6,
 +	0x9339,	0x8318,	0xb37b,	0xa35a,	0xd3bd,	0xc39c,	0xf3ff,	0xe3de,
 +	0x2462,	0x3443,	0x0420,	0x1401,	0x64e6,	0x74c7,	0x44a4,	0x5485,
 +	0xa56a,	0xb54b,	0x8528,	0x9509,	0xe5ee,	0xf5cf,	0xc5ac,	0xd58d,
 +	0x3653,	0x2672,	0x1611,	0x0630,	0x76d7,	0x66f6,	0x5695,	0x46b4,
 +	0xb75b,	0xa77a,	0x9719,	0x8738,	0xf7df,	0xe7fe,	0xd79d,	0xc7bc,
 +	0x48c4,	0x58e5,	0x6886,	0x78a7,	0x0840,	0x1861,	0x2802,	0x3823,
 +	0xc9cc,	0xd9ed,	0xe98e,	0xf9af,	0x8948,	0x9969,	0xa90a,	0xb92b,
 +	0x5af5,	0x4ad4,	0x7ab7,	0x6a96,	0x1a71,	0x0a50,	0x3a33,	0x2a12,
 +	0xdbfd,	0xcbdc,	0xfbbf,	0xeb9e,	0x9b79,	0x8b58,	0xbb3b,	0xab1a,
 +	0x6ca6,	0x7c87,	0x4ce4,	0x5cc5,	0x2c22,	0x3c03,	0x0c60,	0x1c41,
 +	0xedae,	0xfd8f,	0xcdec,	0xddcd,	0xad2a,	0xbd0b,	0x8d68,	0x9d49,
 +	0x7e97,	0x6eb6,	0x5ed5,	0x4ef4,	0x3e13,	0x2e32,	0x1e51,	0x0e70,
 +	0xff9f,	0xefbe,	0xdfdd,	0xcffc,	0xbf1b,	0xaf3a,	0x9f59,	0x8f78,
 +	0x9188,	0x81a9,	0xb1ca,	0xa1eb,	0xd10c,	0xc12d,	0xf14e,	0xe16f,
 +	0x1080,	0x00a1,	0x30c2,	0x20e3,	0x5004,	0x4025,	0x7046,	0x6067,
 +	0x83b9,	0x9398,	0xa3fb,	0xb3da,	0xc33d,	0xd31c,	0xe37f,	0xf35e,
 +	0x02b1,	0x1290,	0x22f3,	0x32d2,	0x4235,	0x5214,	0x6277,	0x7256,
 +	0xb5ea,	0xa5cb,	0x95a8,	0x8589,	0xf56e,	0xe54f,	0xd52c,	0xc50d,
 +	0x34e2,	0x24c3,	0x14a0,	0x0481,	0x7466,	0x6447,	0x5424,	0x4405,
 +	0xa7db,	0xb7fa,	0x8799,	0x97b8,	0xe75f,	0xf77e,	0xc71d,	0xd73c,
 +	0x26d3,	0x36f2,	0x0691,	0x16b0,	0x6657,	0x7676,	0x4615,	0x5634,
 +	0xd94c,	0xc96d,	0xf90e,	0xe92f,	0x99c8,	0x89e9,	0xb98a,	0xa9ab,
 +	0x5844,	0x4865,	0x7806,	0x6827,	0x18c0,	0x08e1,	0x3882,	0x28a3,
 +	0xcb7d,	0xdb5c,	0xeb3f,	0xfb1e,	0x8bf9,	0x9bd8,	0xabbb,	0xbb9a,
 +	0x4a75,	0x5a54,	0x6a37,	0x7a16,	0x0af1,	0x1ad0,	0x2ab3,	0x3a92,
 +	0xfd2e,	0xed0f,	0xdd6c,	0xcd4d,	0xbdaa,	0xad8b,	0x9de8,	0x8dc9,
 +	0x7c26,	0x6c07,	0x5c64,	0x4c45,	0x3ca2,	0x2c83,	0x1ce0,	0x0cc1,
 +	0xef1f,	0xff3e,	0xcf5d,	0xdf7c,	0xaf9b,	0xbfba,	0x8fd9,	0x9ff8,
 +	0x6e17,	0x7e36,	0x4e55,	0x5e74,	0x2e93,	0x3eb2,	0x0ed1,	0x1ef0
 +};
 +
 +void CRC_Init(unsigned short *crcvalue)
 +{
 +	*crcvalue = CRC_INIT_VALUE;
 +}
 +
 +void CRC_ProcessByte(unsigned short *crcvalue, byte data)
 +{
 +	*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
 +}
 +
 +unsigned short CRC_Value(unsigned short crcvalue)
 +{
 +	return crcvalue ^ CRC_XOR_VALUE;
 +}
 +//=============================================================================
 +
 +/*
 +============
 +CreatePath
 +============
 +*/
 +void	CreatePath (char *path)
 +{
 +	char	*ofs, c;
 +
 +	if (path[1] == ':')
 +		path += 2;
 +
 +	for (ofs = path+1 ; *ofs ; ofs++)
 +	{
 +		c = *ofs;
 +		if (c == '/' || c == '\\')
 +		{	// create the directory
 +			*ofs = 0;
 +			Q_mkdir (path);
 +			*ofs = c;
 +		}
 +	}
 +}
 +
 +
 +/*
 +============
 +QCopyFile
 +
 +  Used to archive source files
 +============
 +*/
 +void QCopyFile (char *from, char *to)
 +{
 +	void	*buffer;
 +	int		length;
 +
 +	length = LoadFile (from, &buffer, 0, 0);
 +	CreatePath (to);
 +	SaveFile (to, buffer, length);
 +	FreeMemory(buffer);
 +}
 +
 +void FS_FreeFile(void *buf)
 +{
 +	FreeMemory(buf);
 +} //end of the function FS_FreeFile
 +
 +int FS_ReadFileAndCache(const char *qpath, void **buffer)
 +{
 +	return LoadFile((char *) qpath, buffer, 0, 0);
 +} //end of the function FS_ReadFileAndCache
 +
 +int FS_FOpenFileRead( const char *filename, FILE **file, qboolean uniqueFILE )
 +{
 +	*file = fopen(filename, "rb");
 +	return (*file != NULL);
 +} //end of the function FS_FOpenFileRead
 diff --git a/code/bspc/l_cmd.h b/code/bspc/l_cmd.h new file mode 100755 index 0000000..b5497b4 --- /dev/null +++ b/code/bspc/l_cmd.h @@ -0,0 +1,157 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +// cmdlib.h
 +
 +#ifndef SIN
 +#define SIN
 +#endif //SIN
 +
 +#ifndef __CMDLIB__
 +#define __CMDLIB__
 +
 +#ifdef _WIN32
 +#pragma warning(disable : 4244)     // MIPS
 +#pragma warning(disable : 4136)     // X86
 +#pragma warning(disable : 4051)     // ALPHA
 +
 +#pragma warning(disable : 4018)     // signed/unsigned mismatch
 +#pragma warning(disable : 4305)     // truncate from double to float
 +#endif
 +
 +#include <stdio.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include <errno.h>
 +#include <ctype.h>
 +#include <time.h>
 +#include <stdarg.h>
 +
 +#ifndef __BYTEBOOL__
 +#define __BYTEBOOL__
 +typedef enum {false, true} qboolean;
 +typedef unsigned char byte;
 +#endif
 +
 +// the dec offsetof macro doesnt work very well...
 +#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
 +
 +
 +// set these before calling CheckParm
 +extern int myargc;
 +extern char **myargv;
 +
 +char *strupr (char *in);
 +char *strlower (char *in);
 +int Q_strncasecmp (char *s1, char *s2, int n);
 +int Q_strcasecmp (char *s1, char *s2);
 +void Q_getwd (char *out);
 +
 +int Q_filelength (FILE *f);
 +int	FileTime (char *path);
 +
 +void	Q_mkdir (char *path);
 +
 +extern	char		qdir[1024];
 +extern	char		gamedir[1024];
 +void SetQdirFromPath (char *path);
 +char *ExpandArg (char *path);	// from cmd line
 +char *ExpandPath (char *path);	// from scripts
 +char *ExpandPathAndArchive (char *path);
 +
 +
 +double I_FloatTime (void);
 +
 +void Error(char *error, ...);
 +void Warning(char *warning, ...);
 +
 +int		CheckParm (char *check);
 +
 +FILE	*SafeOpenWrite (char *filename);
 +FILE	*SafeOpenRead (char *filename);
 +void	SafeRead (FILE *f, void *buffer, int count);
 +void	SafeWrite (FILE *f, void *buffer, int count);
 +
 +int LoadFile (char *filename, void **bufferptr, int offset, int length);
 +int TryLoadFile (char *filename, void **bufferptr);
 +void SaveFile (char *filename, void *buffer, int count);
 +qboolean	FileExists (char *filename);
 +
 +void 	DefaultExtension (char *path, char *extension);
 +void 	DefaultPath (char *path, char *basepath);
 +void 	StripFilename (char *path);
 +void 	StripExtension (char *path);
 +
 +void 	ExtractFilePath (char *path, char *dest);
 +void 	ExtractFileBase (char *path, char *dest);
 +void	ExtractFileExtension (char *path, char *dest);
 +
 +int 	ParseNum (char *str);
 +
 +short	BigShort (short l);
 +short	LittleShort (short l);
 +int		BigLong (int l);
 +int		LittleLong (int l);
 +float	BigFloat (float l);
 +float	LittleFloat (float l);
 +
 +#ifdef SIN
 +unsigned short	BigUnsignedShort (unsigned short l);
 +unsigned short	LittleUnsignedShort (unsigned short l);
 +unsigned	      BigUnsigned (unsigned l);
 +unsigned	      LittleUnsigned (unsigned l);
 +#endif
 +
 +
 +char *COM_Parse (char *data);
 +
 +extern	char		com_token[1024];
 +extern	qboolean	com_eof;
 +
 +char *copystring(char *s);
 +
 +
 +void CRC_Init(unsigned short *crcvalue);
 +void CRC_ProcessByte(unsigned short *crcvalue, byte data);
 +unsigned short CRC_Value(unsigned short crcvalue);
 +
 +void	CreatePath (char *path);
 +void	QCopyFile (char *from, char *to);
 +
 +extern	qboolean		archive;
 +extern	char			archivedir[1024];
 +
 +
 +extern	qboolean verbose;
 +void qprintf (char *format, ...);
 +
 +void ExpandWildcards (int *argc, char ***argv);
 +
 +
 +// for compression routines
 +typedef struct
 +{
 +	byte	*data;
 +	int		count;
 +} cblock_t;
 +
 +#endif
 +
 diff --git a/code/bspc/l_log.c b/code/bspc/l_log.c new file mode 100755 index 0000000..b685ed3 --- /dev/null +++ b/code/bspc/l_log.c @@ -0,0 +1,215 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <string.h>
 +
 +#include "qbsp.h"
 +
 +#define MAX_LOGFILENAMESIZE		1024
 +
 +typedef struct logfile_s
 +{
 +	char filename[MAX_LOGFILENAMESIZE];
 +	FILE *fp;
 +	int numwrites;
 +} logfile_t;
 +
 +logfile_t logfile;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_Open(char *filename)
 +{
 +	if (!filename || !strlen(filename))
 +	{
 +		printf("openlog <filename>\n");
 +		return;
 +	} //end if
 +	if (logfile.fp)
 +	{
 +		printf("log file %s is already opened\n", logfile.filename);
 +		return;
 +	} //end if
 +	logfile.fp = fopen(filename, "wb");
 +	if (!logfile.fp)
 +	{
 +		printf("can't open the log file %s\n", filename);
 +		return;
 +	} //end if
 +	strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE);
 +	printf("Opened log %s\n", logfile.filename);
 +} //end of the function Log_Create
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_Close(void)
 +{
 +	if (!logfile.fp)
 +	{
 +		printf("no log file to close\n");
 +		return;
 +	} //end if
 +	if (fclose(logfile.fp))
 +	{
 +		printf("can't close log file %s\n", logfile.filename);
 +		return;
 +	} //end if
 +	logfile.fp = NULL;
 +	printf("Closed log %s\n", logfile.filename);
 +} //end of the function Log_Close
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_Shutdown(void)
 +{
 +	if (logfile.fp) Log_Close();
 +} //end of the function Log_Shutdown
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_UnifyEndOfLine(char *buf)
 +{
 +	int i;
 +
 +	for (i = 0; buf[i]; i++)
 +	{
 +		if (buf[i] == '\n')
 +		{
 +			if (i <= 0 || buf[i-1] != '\r')
 +			{
 +				memmove(&buf[i+1], &buf[i], strlen(&buf[i])+1);
 +				buf[i] = '\r';
 +				i++;
 +			} //end if
 +		} //end if
 +	} //end for
 +} //end of the function Log_UnifyEndOfLine
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_Print(char *fmt, ...)
 +{
 +	va_list ap;
 +	char buf[2048];
 +
 +	va_start(ap, fmt);
 +	vsprintf(buf, fmt, ap);
 +	va_end(ap);
 +
 +	if (verbose)
 +	{
 +#ifdef WINBSPC
 +		WinBSPCPrint(buf);
 +#else
 +		printf("%s", buf);
 +#endif //WINBSPS
 +	} //end if
 +
 +	if (logfile.fp)
 +	{
 +		Log_UnifyEndOfLine(buf);
 +		fprintf(logfile.fp, "%s", buf);
 +		fflush(logfile.fp);
 +	} //end if
 +} //end of the function Log_Print
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_Write(char *fmt, ...)
 +{
 +	va_list ap;
 +	char buf[2048];
 +
 +	if (!logfile.fp) return;
 +	va_start(ap, fmt);
 +	vsprintf(buf, fmt, ap);
 +	va_end(ap);
 +	Log_UnifyEndOfLine(buf);
 +	fprintf(logfile.fp, "%s", buf);
 +	fflush(logfile.fp);
 +} //end of the function Log_Write
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_WriteTimeStamped(char *fmt, ...)
 +{
 +	va_list ap;
 +
 +	if (!logfile.fp) return;
 +/*	fprintf(logfile.fp, "%d   %02d:%02d:%02d:%02d   ",
 +					logfile.numwrites,
 +					(int) (botlibglobals.time / 60 / 60),
 +					(int) (botlibglobals.time / 60),
 +					(int) (botlibglobals.time),
 +					(int) ((int) (botlibglobals.time * 100)) -
 +							((int) botlibglobals.time) * 100);*/
 +	va_start(ap, fmt);
 +	vfprintf(logfile.fp, fmt, ap);
 +	va_end(ap);
 +	logfile.numwrites++;
 +	fflush(logfile.fp);
 +} //end of the function Log_Write
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +FILE *Log_FileStruct(void)
 +{
 +	return logfile.fp;
 +} //end of the function Log_FileStruct
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Log_Flush(void)
 +{
 +	if (logfile.fp) fflush(logfile.fp);
 +} //end of the function Log_Flush
 +
 diff --git a/code/bspc/l_log.h b/code/bspc/l_log.h new file mode 100755 index 0000000..eff7bf6 --- /dev/null +++ b/code/bspc/l_log.h @@ -0,0 +1,42 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//open a log file
 +void Log_Open(char *filename);
 +//close the current log file
 +void Log_Close(void);
 +//close log file if present
 +void Log_Shutdown(void);
 +//print on stdout and write to the current opened log file
 +void Log_Print(char *fmt, ...);
 +//write to the current opened log file
 +void Log_Write(char *fmt, ...);
 +//write to the current opened log file with a time stamp
 +void Log_WriteTimeStamped(char *fmt, ...);
 +//returns the log file structure
 +FILE *Log_FileStruct(void);
 +//flush log file
 +void Log_Flush(void);
 +
 +#ifdef WINBSPC
 +void WinBSPCPrint(char *str);
 +#endif //WINBSPC
 diff --git a/code/bspc/l_math.c b/code/bspc/l_math.c new file mode 100755 index 0000000..caf3419 --- /dev/null +++ b/code/bspc/l_math.c @@ -0,0 +1,289 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +// mathlib.c -- math primitives
 +
 +#include "l_cmd.h"
 +#include "l_math.h"
 +
 +vec3_t vec3_origin = {0,0,0};
 +
 +void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
 +{
 +	float		angle;
 +	static float		sr, sp, sy, cr, cp, cy;
 +	// static to help MS compiler fp bugs
 +
 +	angle = angles[YAW] * (M_PI*2 / 360);
 +	sy = sin(angle);
 +	cy = cos(angle);
 +	angle = angles[PITCH] * (M_PI*2 / 360);
 +	sp = sin(angle);
 +	cp = cos(angle);
 +	angle = angles[ROLL] * (M_PI*2 / 360);
 +	sr = sin(angle);
 +	cr = cos(angle);
 +
 +	if (forward)
 +	{
 +		forward[0] = cp*cy;
 +		forward[1] = cp*sy;
 +		forward[2] = -sp;
 +	}
 +	if (right)
 +	{
 +		right[0] = (-1*sr*sp*cy+-1*cr*-sy);
 +		right[1] = (-1*sr*sp*sy+-1*cr*cy);
 +		right[2] = -1*sr*cp;
 +	}
 +	if (up)
 +	{
 +		up[0] = (cr*sp*cy+-sr*-sy);
 +		up[1] = (cr*sp*sy+-sr*cy);
 +		up[2] = cr*cp;
 +	}
 +}
 +
 +/*
 +=================
 +RadiusFromBounds
 +=================
 +*/
 +float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ) {
 +	int		i;
 +	vec3_t	corner;
 +	float	a, b;
 +
 +	for (i=0 ; i<3 ; i++) {
 +		a = fabs( mins[i] );
 +		b = fabs( maxs[i] );
 +		corner[i] = a > b ? a : b;
 +	}
 +
 +	return VectorLength (corner);
 +}
 +
 +/*
 +================
 +R_ConcatRotations
 +================
 +*/
 +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
 +{
 +	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
 +				in1[0][2] * in2[2][0];
 +	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
 +				in1[0][2] * in2[2][1];
 +	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
 +				in1[0][2] * in2[2][2];
 +	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
 +				in1[1][2] * in2[2][0];
 +	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
 +				in1[1][2] * in2[2][1];
 +	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
 +				in1[1][2] * in2[2][2];
 +	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
 +				in1[2][2] * in2[2][0];
 +	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
 +				in1[2][2] * in2[2][1];
 +	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
 +				in1[2][2] * in2[2][2];
 +}
 +
 +void AxisClear( vec3_t axis[3] ) {
 +	axis[0][0] = 1;
 +	axis[0][1] = 0;
 +	axis[0][2] = 0;
 +	axis[1][0] = 0;
 +	axis[1][1] = 1;
 +	axis[1][2] = 0;
 +	axis[2][0] = 0;
 +	axis[2][1] = 0;
 +	axis[2][2] = 1;
 +}
 +
 +float VectorLengthSquared(vec3_t v) {
 +	return DotProduct(v, v);
 +}
 +
 +double VectorLength(vec3_t v)
 +{
 +	int		i;
 +	double	length;
 +	
 +	length = 0;
 +	for (i=0 ; i< 3 ; i++)
 +		length += v[i]*v[i];
 +	length = sqrt (length);		// FIXME
 +
 +	return length;
 +}
 +
 +qboolean VectorCompare (vec3_t v1, vec3_t v2)
 +{
 +	int		i;
 +	
 +	for (i=0 ; i<3 ; i++)
 +		if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
 +			return false;
 +			
 +	return true;
 +}
 +
 +vec_t Q_rint (vec_t in)
 +{
 +	return floor(in + 0.5);
 +}
 +
 +void CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross)
 +{
 +	cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
 +	cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
 +	cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
 +}
 +
 +void _VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc)
 +{
 +	vc[0] = va[0] + scale*vb[0];
 +	vc[1] = va[1] + scale*vb[1];
 +	vc[2] = va[2] + scale*vb[2];
 +}
 +
 +vec_t _DotProduct (vec3_t v1, vec3_t v2)
 +{
 +	return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
 +}
 +
 +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
 +{
 +	out[0] = va[0]-vb[0];
 +	out[1] = va[1]-vb[1];
 +	out[2] = va[2]-vb[2];
 +}
 +
 +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
 +{
 +	out[0] = va[0]+vb[0];
 +	out[1] = va[1]+vb[1];
 +	out[2] = va[2]+vb[2];
 +}
 +
 +void _VectorCopy (vec3_t in, vec3_t out)
 +{
 +	out[0] = in[0];
 +	out[1] = in[1];
 +	out[2] = in[2];
 +}
 +
 +void _VectorScale (vec3_t v, vec_t scale, vec3_t out)
 +{
 +	out[0] = v[0] * scale;
 +	out[1] = v[1] * scale;
 +	out[2] = v[2] * scale;
 +}
 +
 +vec_t VectorNormalize(vec3_t inout)
 +{
 +	vec_t	length, ilength;
 +
 +	length = sqrt (inout[0]*inout[0] + inout[1]*inout[1] + inout[2]*inout[2]);
 +	if (length == 0)
 +	{
 +		VectorClear (inout);
 +		return 0;
 +	}
 +
 +	ilength = 1.0/length;
 +	inout[0] = inout[0]*ilength;
 +	inout[1] = inout[1]*ilength;
 +	inout[2] = inout[2]*ilength;
 +
 +	return length;
 +}
 +
 +vec_t VectorNormalize2(const vec3_t in, vec3_t out)
 +{
 +	vec_t	length, ilength;
 +
 +	length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
 +	if (length == 0)
 +	{
 +		VectorClear (out);
 +		return 0;
 +	}
 +
 +	ilength = 1.0/length;
 +	out[0] = in[0]*ilength;
 +	out[1] = in[1]*ilength;
 +	out[2] = in[2]*ilength;
 +
 +	return length;
 +}
 +
 +vec_t ColorNormalize (vec3_t in, vec3_t out)
 +{
 +	float	max, scale;
 +
 +	max = in[0];
 +	if (in[1] > max)
 +		max = in[1];
 +	if (in[2] > max)
 +		max = in[2];
 +
 +	if (max == 0)
 +		return 0;
 +
 +	scale = 1.0 / max;
 +
 +	VectorScale (in, scale, out);
 +
 +	return max;
 +}
 +
 +
 +
 +void VectorInverse (vec3_t v)
 +{
 +	v[0] = -v[0];
 +	v[1] = -v[1];
 +	v[2] = -v[2];
 +}
 +
 +void ClearBounds(vec3_t mins, vec3_t maxs)
 +{
 +	mins[0] = mins[1] = mins[2] = 99999;
 +	maxs[0] = maxs[1] = maxs[2] = -99999;
 +}
 +
 +void AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs)
 +{
 +	int		i;
 +	vec_t	val;
 +
 +	for (i=0 ; i<3 ; i++)
 +	{
 +		val = v[i];
 +		if (val < mins[i])
 +			mins[i] = val;
 +		if (val > maxs[i])
 +			maxs[i] = val;
 +	}
 +}
 diff --git a/code/bspc/l_math.h b/code/bspc/l_math.h new file mode 100755 index 0000000..1df7b61 --- /dev/null +++ b/code/bspc/l_math.h @@ -0,0 +1,93 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +#ifndef __MATHLIB__
 +#define __MATHLIB__
 +
 +// mathlib.h
 +
 +#include <math.h>
 +
 +#ifdef DOUBLEVEC_T
 +typedef double vec_t;
 +#else
 +typedef float vec_t;
 +#endif
 +typedef vec_t vec3_t[3];
 +typedef vec_t vec4_t[4];
 +
 +#define	SIDE_FRONT		0
 +#define	SIDE_ON			2
 +#define	SIDE_BACK		1
 +#define	SIDE_CROSS		-2
 +
 +#define PITCH		0
 +#define YAW			1
 +#define ROLL		2
 +
 +#define	Q_PI	3.14159265358979323846
 +
 +#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
 +
 +#ifndef M_PI
 +#define M_PI		3.14159265358979323846	// matches value in gcc v2 math.h
 +#endif
 +
 +extern vec3_t vec3_origin;
 +
 +#define	EQUAL_EPSILON	0.001
 +
 +qboolean VectorCompare (vec3_t v1, vec3_t v2);
 +
 +#define DotProduct(x,y)			(x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
 +#define VectorSubtract(a,b,c)	{c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
 +#define VectorAdd(a,b,c)		{c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
 +#define VectorCopy(a,b)			{b[0]=a[0];b[1]=a[1];b[2]=a[2];}
 +#define Vector4Copy(a,b)		{b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];}
 +#define	VectorScale(v, s, o)	((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))
 +#define VectorClear(x)			{x[0] = x[1] = x[2] = 0;}
 +#define VectorNegate(x, y)		{y[0]=-x[0];y[1]=-x[1];y[2]=-x[2];}
 +#define	VectorMA(v, s, b, o)	((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))
 +
 +vec_t Q_rint (vec_t in);
 +vec_t _DotProduct (vec3_t v1, vec3_t v2);
 +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
 +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
 +void _VectorCopy (vec3_t in, vec3_t out);
 +void _VectorScale (vec3_t v, vec_t scale, vec3_t out);
 +void _VectorMA(vec3_t va, double scale, vec3_t vb, vec3_t vc);
 +
 +double VectorLength(vec3_t v);
 +void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross);
 +vec_t VectorNormalize(vec3_t inout);
 +vec_t ColorNormalize(vec3_t in, vec3_t out);
 +vec_t VectorNormalize2(const vec3_t v, vec3_t out);
 +void VectorInverse (vec3_t v);
 +
 +void ClearBounds (vec3_t mins, vec3_t maxs);
 +void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs);
 +
 +void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
 +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);
 +void RotatePoint(vec3_t point, float matrix[3][3]);
 +void CreateRotationMatrix(vec3_t angles, float matrix[3][3]);
 +
 +#endif
 diff --git a/code/bspc/l_mem.c b/code/bspc/l_mem.c new file mode 100755 index 0000000..831543e --- /dev/null +++ b/code/bspc/l_mem.c @@ -0,0 +1,441 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_log.h"
 +
 +int allocedmemory;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintMemorySize(unsigned long size)
 +{
 +	unsigned long number1, number2, number3;
 +	number1 = size >> 20;
 +	number2 = (size & 0xFFFFF) >> 10;
 +	number3 = (size & 0x3FF);
 +	if (number1) Log_Print("%ld MB", number1);
 +	if (number1 && number2) Log_Print(" and ");
 +	if (number2) Log_Print("%ld KB", number2);
 +	if (number2 && number3) Log_Print(" and ");
 +	if (number3) Log_Print("%ld bytes", number3);
 +} //end of the function PrintFileSize
 +
 +#ifndef MEMDEBUG
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int MemorySize(void *ptr)
 +{
 +#if defined(WIN32) || defined(_WIN32)
 +	#ifdef __WATCOMC__
 +		//Intel 32 bits memory addressing, 16 bytes aligned
 +	return (_msize(ptr) + 15) >> 4 << 4;
 +	#else
 +	return _msize(ptr);
 +	#endif
 +#else
 +	return 0;
 +#endif
 +} //end of the function MemorySize
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *GetClearedMemory(int size)
 +{
 +	void *ptr;
 +
 +	ptr = (void *) malloc(size);
 +	if (!ptr) Error("out of memory");
 +	memset(ptr, 0, size);
 +	allocedmemory += MemorySize(ptr);
 +	return ptr;
 +} //end of the function GetClearedMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *GetMemory(unsigned long size)
 +{
 +	void *ptr;
 +	ptr = malloc(size);
 +	if (!ptr) Error("out of memory");
 +	allocedmemory += MemorySize(ptr);
 +	return ptr;
 +} //end of the function GetMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FreeMemory(void *ptr)
 +{
 +	allocedmemory -= MemorySize(ptr);
 +	free(ptr);
 +} //end of the function FreeMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int TotalAllocatedMemory(void)
 +{
 +	return allocedmemory;
 +} //end of the function TotalAllocatedMemory
 +
 +#else
 +
 +#define MEM_ID		0x12345678l
 +
 +int totalmemorysize;
 +int numblocks;
 +
 +typedef struct memoryblock_s
 +{
 +	unsigned long int id;
 +	void *ptr;
 +	int size;
 +#ifdef MEMDEBUG
 +	char *label;
 +	char *file;
 +	int line;
 +#endif //MEMDEBUG
 +	struct memoryblock_s *prev, *next;
 +} memoryblock_t;
 +
 +memoryblock_t *memory;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void LinkMemoryBlock(memoryblock_t *block)
 +{
 +	block->prev = NULL;
 +	block->next = memory;
 +	if (memory) memory->prev = block;
 +	memory = block;
 +} //end of the function LinkMemoryBlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void UnlinkMemoryBlock(memoryblock_t *block)
 +{
 +	if (block->prev) block->prev->next = block->next;
 +	else memory = block->next;
 +	if (block->next) block->next->prev = block->prev;
 +} //end of the function UnlinkMemoryBlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#ifdef MEMDEBUG
 +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line)
 +#else
 +void *GetMemory(unsigned long size)
 +#endif //MEMDEBUG
 +{
 +	void *ptr;
 +	memoryblock_t *block;
 +
 +	ptr = malloc(size + sizeof(memoryblock_t));
 +	block = (memoryblock_t *) ptr;
 +	block->id = MEM_ID;
 +	block->ptr = (char *) ptr + sizeof(memoryblock_t);
 +	block->size = size + sizeof(memoryblock_t);
 +#ifdef MEMDEBUG
 +	block->label = label;
 +	block->file = file;
 +	block->line = line;
 +#endif //MEMDEBUG
 +	LinkMemoryBlock(block);
 +	totalmemorysize += block->size;
 +	numblocks++;
 +	return block->ptr;
 +} //end of the function GetMemoryDebug
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#ifdef MEMDEBUG
 +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line)
 +#else
 +void *GetClearedMemory(unsigned long size)
 +#endif //MEMDEBUG
 +{
 +	void *ptr;
 +#ifdef MEMDEBUG
 +	ptr = GetMemoryDebug(size, label, file, line);
 +#else
 +	ptr = GetMemory(size);
 +#endif //MEMDEBUG
 +	memset(ptr, 0, size);
 +	return ptr;
 +} //end of the function GetClearedMemoryLabelled
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *GetClearedHunkMemory(unsigned long size)
 +{
 +	return GetClearedMemory(size);
 +} //end of the function GetClearedHunkMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *GetHunkMemory(unsigned long size)
 +{
 +	return GetMemory(size);
 +} //end of the function GetHunkMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +memoryblock_t *BlockFromPointer(void *ptr, char *str)
 +{
 +	memoryblock_t *block;
 +
 +	if (!ptr)
 +	{
 +#ifdef MEMDEBUG
 +		//char *crash = (char *) NULL;
 +		//crash[0] = 1;
 +		Error("%s: NULL pointer\n", str);
 +#endif MEMDEBUG
 +		return NULL;
 +	} //end if
 +	block = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t));
 +	if (block->id != MEM_ID)
 +	{
 +		Error("%s: invalid memory block\n", str);
 +	} //end if
 +	if (block->ptr != ptr)
 +	{
 +		
 +		Error("%s: memory block pointer invalid\n", str);
 +	} //end if
 +	return block;
 +} //end of the function BlockFromPointer
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FreeMemory(void *ptr)
 +{
 +	memoryblock_t *block;
 +
 +	block = BlockFromPointer(ptr, "FreeMemory");
 +	if (!block) return;
 +	UnlinkMemoryBlock(block);
 +	totalmemorysize -= block->size;
 +	numblocks--;
 +	//
 +	free(block);
 +} //end of the function FreeMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int MemoryByteSize(void *ptr)
 +{
 +	memoryblock_t *block;
 +
 +	block = BlockFromPointer(ptr, "MemoryByteSize");
 +	if (!block) return 0;
 +	return block->size;
 +} //end of the function MemoryByteSize
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int MemorySize(void *ptr)
 +{
 +	return MemoryByteSize(ptr);
 +} //end of the function MemorySize
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintUsedMemorySize(void)
 +{
 +	printf("total botlib memory: %d KB\n", totalmemorysize >> 10);
 +	printf("total memory blocks: %d\n", numblocks);
 +} //end of the function PrintUsedMemorySize
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintMemoryLabels(void)
 +{
 +	memoryblock_t *block;
 +	int i;
 +
 +	PrintUsedMemorySize();
 +	i = 0;
 +	for (block = memory; block; block = block->next)
 +	{
 +#ifdef MEMDEBUG
 +		Log_Write("%6d, %p, %8d: %24s line %6d: %s", i, block->ptr, block->size, block->file, block->line, block->label);
 +#endif //MEMDEBUG
 +		i++;
 +	} //end for
 +} //end of the function PrintMemoryLabels
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void DumpMemory(void)
 +{
 +	memoryblock_t *block;
 +
 +	for (block = memory; block; block = memory)
 +	{
 +		FreeMemory(block->ptr);
 +	} //end for
 +	totalmemorysize = 0;
 +} //end of the function DumpMemory
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int TotalAllocatedMemory(void)
 +{
 +	return totalmemorysize;
 +} //end of the function TotalAllocatedMemory
 +#endif
 +
 +//===========================================================================
 +// Q3 Hunk and Z_ memory management
 +//===========================================================================
 +
 +typedef struct memhunk_s
 +{
 +	void *ptr;
 +	struct memhunk_s *next;
 +} memhunk_t;
 +
 +memhunk_t *memhunk_high;
 +memhunk_t *memhunk_low;
 +int memhunk_high_size = 16 * 1024 * 1024;
 +int memhunk_low_size = 0;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Hunk_ClearHigh(void)
 +{
 +	memhunk_t *h, *nexth;
 +
 +	for (h = memhunk_high; h; h = nexth)
 +	{
 +		nexth = h->next;
 +		FreeMemory(h);
 +	} //end for
 +	memhunk_high = NULL;
 +	memhunk_high_size = 16 * 1024 * 1024;
 +} //end of the function Hunk_ClearHigh
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *Hunk_Alloc(int size)
 +{
 +	memhunk_t *h;
 +
 +	if (!size) return (void *) memhunk_high_size;
 +	//
 +	h = GetClearedMemory(size + sizeof(memhunk_t));
 +	h->ptr = (char *) h + sizeof(memhunk_t);
 +	h->next = memhunk_high;
 +	memhunk_high = h;
 +	memhunk_high_size -= size;
 +	return h->ptr;
 +} //end of the function Hunk_Alloc
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void *Z_Malloc(int size)
 +{
 +	return GetClearedMemory(size);
 +} //end of the function Z_Malloc
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Z_Free (void *ptr)
 +{
 +	FreeMemory(ptr);
 +} //end of the function Z_Free
 diff --git a/code/bspc/l_mem.h b/code/bspc/l_mem.h new file mode 100755 index 0000000..d517743 --- /dev/null +++ b/code/bspc/l_mem.h @@ -0,0 +1,51 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +
 +//=============================================================================
 +
 +// memory.h
 +//#define MEMDEBUG
 +#undef MEMDEBUG
 +
 +#ifndef MEMDEBUG
 +
 +void *GetClearedMemory(int size);
 +void *GetMemory(unsigned long size);
 +
 +#else
 +
 +#define GetMemory(size)				GetMemoryDebug(size, #size, __FILE__, __LINE__);
 +#define GetClearedMemory(size)	GetClearedMemoryDebug(size, #size, __FILE__, __LINE__);
 +//allocate a memory block of the given size
 +void *GetMemoryDebug(unsigned long size, char *label, char *file, int line);
 +//allocate a memory block of the given size and clear it
 +void *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line);
 +//
 +void PrintMemoryLabels(void);
 +#endif //MEMDEBUG
 +
 +void FreeMemory(void *ptr);
 +int MemorySize(void *ptr);
 +void PrintMemorySize(unsigned long size);
 +int TotalAllocatedMemory(void);
 +
 diff --git a/code/bspc/l_poly.c b/code/bspc/l_poly.c new file mode 100755 index 0000000..fea2bef --- /dev/null +++ b/code/bspc/l_poly.c @@ -0,0 +1,1411 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include <malloc.h>
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_poly.h"
 +#include "l_log.h"
 +#include "l_mem.h"
 +
 +#define	BOGUS_RANGE		65535
 +
 +extern int numthreads;
 +
 +// counters are only bumped when running single threaded,
 +// because they are an awefull coherence problem
 +int c_active_windings;
 +int c_peak_windings;
 +int c_winding_allocs;
 +int c_winding_points;
 +int c_windingmemory;
 +int c_peak_windingmemory;
 +
 +char windingerror[1024];
 +
 +void pw(winding_t *w)
 +{
 +	int		i;
 +	for (i=0 ; i<w->numpoints ; i++)
 +		printf ("(%5.3f, %5.3f, %5.3f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
 +}
 +
 +
 +void ResetWindings(void)
 +{
 +	c_active_windings = 0;
 +	c_peak_windings = 0;
 +	c_winding_allocs = 0;
 +	c_winding_points = 0;
 +	c_windingmemory = 0;
 +	c_peak_windingmemory = 0;
 +
 +	strcpy(windingerror, "");
 +} //end of the function ResetWindings
 +/*
 +=============
 +AllocWinding
 +=============
 +*/
 +winding_t *AllocWinding (int points)
 +{
 +	winding_t	*w;
 +	int			s;
 +
 +	s = sizeof(vec_t)*3*points + sizeof(int);
 +	w = GetMemory(s);
 +	memset(w, 0, s);
 +
 +	if (numthreads == 1)
 +	{
 +		c_winding_allocs++;
 +		c_winding_points += points;
 +		c_active_windings++;
 +		if (c_active_windings > c_peak_windings)
 +			c_peak_windings = c_active_windings;
 +		c_windingmemory += MemorySize(w);
 +		if (c_windingmemory > c_peak_windingmemory)
 +			c_peak_windingmemory = c_windingmemory;
 +	} //end if
 +	return w;
 +} //end of the function AllocWinding
 +
 +void FreeWinding (winding_t *w)
 +{
 +	if (*(unsigned *)w == 0xdeaddead)
 +		Error ("FreeWinding: freed a freed winding");
 +
 +	if (numthreads == 1)
 +	{
 +		c_active_windings--;
 +		c_windingmemory -= MemorySize(w);
 +	} //end if
 +
 +	*(unsigned *)w = 0xdeaddead;
 +
 +	FreeMemory(w);
 +} //end of the function FreeWinding
 +
 +int WindingMemory(void)
 +{
 +	return c_windingmemory;
 +} //end of the function WindingMemory
 +
 +int WindingPeakMemory(void)
 +{
 +	return c_peak_windingmemory;
 +} //end of the function WindingPeakMemory
 +
 +int ActiveWindings(void)
 +{
 +	return c_active_windings;
 +} //end of the function ActiveWindings
 +/*
 +============
 +RemoveColinearPoints
 +============
 +*/
 +int	c_removed;
 +
 +void RemoveColinearPoints (winding_t *w)
 +{
 +	int		i, j, k;
 +	vec3_t	v1, v2;
 +	int		nump;
 +	vec3_t	p[MAX_POINTS_ON_WINDING];
 +
 +	nump = 0;
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		j = (i+1)%w->numpoints;
 +		k = (i+w->numpoints-1)%w->numpoints;
 +		VectorSubtract (w->p[j], w->p[i], v1);
 +		VectorSubtract (w->p[i], w->p[k], v2);
 +		VectorNormalize(v1);
 +		VectorNormalize(v2);
 +		if (DotProduct(v1, v2) < 0.999)
 +		{
 +			if (nump >= MAX_POINTS_ON_WINDING)
 +				Error("RemoveColinearPoints: MAX_POINTS_ON_WINDING");
 +			VectorCopy (w->p[i], p[nump]);
 +			nump++;
 +		}
 +	}
 +
 +	if (nump == w->numpoints)
 +		return;
 +
 +	if (numthreads == 1)
 +		c_removed += w->numpoints - nump;
 +	w->numpoints = nump;
 +	memcpy (w->p, p, nump*sizeof(p[0]));
 +}
 +
 +/*
 +============
 +WindingPlane
 +============
 +*/
 +void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
 +{
 +	vec3_t v1, v2;
 +	int i;
 +
 +	//find two vectors each longer than 0.5 units
 +	for (i = 0; i < w->numpoints; i++)
 +	{
 +		VectorSubtract(w->p[(i+1) % w->numpoints], w->p[i], v1);
 +		VectorSubtract(w->p[(i+2) % w->numpoints], w->p[i], v2);
 +		if (VectorLength(v1) > 0.5 && VectorLength(v2) > 0.5) break;
 +	} //end for
 +	CrossProduct(v2, v1, normal);
 +	VectorNormalize(normal);
 +	*dist = DotProduct(w->p[0], normal);
 +} //end of the function WindingPlane
 +
 +/*
 +=============
 +WindingArea
 +=============
 +*/
 +vec_t	WindingArea (winding_t *w)
 +{
 +	int		i;
 +	vec3_t	d1, d2, cross;
 +	vec_t	total;
 +
 +	total = 0;
 +	for (i=2 ; i<w->numpoints ; i++)
 +	{
 +		VectorSubtract (w->p[i-1], w->p[0], d1);
 +		VectorSubtract (w->p[i], w->p[0], d2);
 +		CrossProduct (d1, d2, cross);
 +		total += 0.5 * VectorLength ( cross );
 +	}
 +	return total;
 +}
 +
 +void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
 +{
 +	vec_t	v;
 +	int		i,j;
 +
 +	mins[0] = mins[1] = mins[2] = 99999;
 +	maxs[0] = maxs[1] = maxs[2] = -99999;
 +
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			v = w->p[i][j];
 +			if (v < mins[j])
 +				mins[j] = v;
 +			if (v > maxs[j])
 +				maxs[j] = v;
 +		}
 +	}
 +}
 +
 +/*
 +=============
 +WindingCenter
 +=============
 +*/
 +void	WindingCenter (winding_t *w, vec3_t center)
 +{
 +	int		i;
 +	float	scale;
 +
 +	VectorCopy (vec3_origin, center);
 +	for (i=0 ; i<w->numpoints ; i++)
 +		VectorAdd (w->p[i], center, center);
 +
 +	scale = 1.0/w->numpoints;
 +	VectorScale (center, scale, center);
 +}
 +
 +/*
 +=================
 +BaseWindingForPlane
 +=================
 +*/
 +winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
 +{
 +	int		i, x;
 +	vec_t	max, v;
 +	vec3_t	org, vright, vup;
 +	winding_t	*w;
 +	
 +// find the major axis
 +
 +	max = -BOGUS_RANGE;
 +	x = -1;
 +	for (i=0 ; i<3; i++)
 +	{
 +		v = fabs(normal[i]);
 +		if (v > max)
 +		{
 +			x = i;
 +			max = v;
 +		}
 +	}
 +	if (x==-1)
 +		Error ("BaseWindingForPlane: no axis found");
 +		
 +	VectorCopy (vec3_origin, vup);	
 +	switch (x)
 +	{
 +	case 0:
 +	case 1:
 +		vup[2] = 1;
 +		break;		
 +	case 2:
 +		vup[0] = 1;
 +		break;		
 +	}
 +
 +	v = DotProduct (vup, normal);
 +	VectorMA (vup, -v, normal, vup);
 +	VectorNormalize (vup);
 +		
 +	VectorScale (normal, dist, org);
 +	
 +	CrossProduct (vup, normal, vright);
 +	
 +	VectorScale (vup, BOGUS_RANGE, vup);
 +	VectorScale (vright, BOGUS_RANGE, vright);
 +
 +// project a really big	axis aligned box onto the plane
 +	w = AllocWinding (4);
 +	
 +	VectorSubtract (org, vright, w->p[0]);
 +	VectorAdd (w->p[0], vup, w->p[0]);
 +	
 +	VectorAdd (org, vright, w->p[1]);
 +	VectorAdd (w->p[1], vup, w->p[1]);
 +	
 +	VectorAdd (org, vright, w->p[2]);
 +	VectorSubtract (w->p[2], vup, w->p[2]);
 +	
 +	VectorSubtract (org, vright, w->p[3]);
 +	VectorSubtract (w->p[3], vup, w->p[3]);
 +	
 +	w->numpoints = 4;
 +	
 +	return w;	
 +}
 +
 +/*
 +==================
 +CopyWinding
 +==================
 +*/
 +winding_t *CopyWinding (winding_t *w)
 +{
 +	int			size;
 +	winding_t	*c;
 +
 +	c = AllocWinding (w->numpoints);
 +	size = (int)((winding_t *)0)->p[w->numpoints];
 +	memcpy (c, w, size);
 +	return c;
 +}
 +
 +/*
 +==================
 +ReverseWinding
 +==================
 +*/
 +winding_t	*ReverseWinding (winding_t *w)
 +{
 +	int			i;
 +	winding_t	*c;
 +
 +	c = AllocWinding (w->numpoints);
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
 +	}
 +	c->numpoints = w->numpoints;
 +	return c;
 +}
 +
 +
 +/*
 +=============
 +ClipWindingEpsilon
 +=============
 +*/
 +void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, 
 +				vec_t epsilon, winding_t **front, winding_t **back)
 +{
 +	vec_t	dists[MAX_POINTS_ON_WINDING+4];
 +	int		sides[MAX_POINTS_ON_WINDING+4];
 +	int		counts[3];
 +	//MrElusive: DOH can't use statics when unsing multithreading!!!
 +	vec_t dot;		// VC 4.2 optimizer bug if not static
 +	int		i, j;
 +	vec_t	*p1, *p2;
 +	vec3_t	mid;
 +	winding_t	*f, *b;
 +	int		maxpts;
 +	
 +	counts[0] = counts[1] = counts[2] = 0;
 +
 +// determine sides for each point
 +	for (i=0 ; i<in->numpoints ; i++)
 +	{
 +		dot = DotProduct (in->p[i], normal);
 +		dot -= dist;
 +		dists[i] = dot;
 +		if (dot > epsilon)
 +			sides[i] = SIDE_FRONT;
 +		else if (dot < -epsilon)
 +			sides[i] = SIDE_BACK;
 +		else
 +		{
 +			sides[i] = SIDE_ON;
 +		}
 +		counts[sides[i]]++;
 +	}
 +	sides[i] = sides[0];
 +	dists[i] = dists[0];
 +	
 +	*front = *back = NULL;
 +
 +	if (!counts[0])
 +	{
 +		*back = CopyWinding (in);
 +		return;
 +	}
 +	if (!counts[1])
 +	{
 +		*front = CopyWinding (in);
 +		return;
 +	}
 +
 +	maxpts = in->numpoints+4;	// cant use counts[0]+2 because
 +								// of fp grouping errors
 +
 +	*front = f = AllocWinding (maxpts);
 +	*back = b = AllocWinding (maxpts);
 +		
 +	for (i=0 ; i<in->numpoints ; i++)
 +	{
 +		p1 = in->p[i];
 +		
 +		if (sides[i] == SIDE_ON)
 +		{
 +			VectorCopy (p1, f->p[f->numpoints]);
 +			f->numpoints++;
 +			VectorCopy (p1, b->p[b->numpoints]);
 +			b->numpoints++;
 +			continue;
 +		}
 +	
 +		if (sides[i] == SIDE_FRONT)
 +		{
 +			VectorCopy (p1, f->p[f->numpoints]);
 +			f->numpoints++;
 +		}
 +		if (sides[i] == SIDE_BACK)
 +		{
 +			VectorCopy (p1, b->p[b->numpoints]);
 +			b->numpoints++;
 +		}
 +
 +		if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
 +			continue;
 +			
 +	// generate a split point
 +		p2 = in->p[(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 (normal[j] == 1)
 +				mid[j] = dist;
 +			else if (normal[j] == -1)
 +				mid[j] = -dist;
 +			else
 +				mid[j] = p1[j] + dot*(p2[j]-p1[j]);
 +		}
 +			
 +		VectorCopy (mid, f->p[f->numpoints]);
 +		f->numpoints++;
 +		VectorCopy (mid, b->p[b->numpoints]);
 +		b->numpoints++;
 +	}
 +	
 +	if (f->numpoints > maxpts || b->numpoints > maxpts)
 +		Error ("ClipWinding: points exceeded estimate");
 +	if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
 +		Error ("ClipWinding: MAX_POINTS_ON_WINDING");
 +}
 +
 +
 +/*
 +=============
 +ChopWindingInPlace
 +=============
 +*/
 +void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
 +{
 +	winding_t *in;
 +	vec_t	dists[MAX_POINTS_ON_WINDING+4];
 +	int sides[MAX_POINTS_ON_WINDING+4];
 +	int counts[3];
 +	//MrElusive: DOH can't use statics when unsing multithreading!!!
 +	vec_t dot;		// VC 4.2 optimizer bug if not static
 +	int i, j;
 +	vec_t *p1, *p2;
 +	vec3_t mid;
 +	winding_t *f;
 +	int maxpts;
 +
 +	in = *inout;
 +	counts[0] = counts[1] = counts[2] = 0;
 +
 +// determine sides for each point
 +	for (i=0 ; i<in->numpoints ; i++)
 +	{
 +		dot = DotProduct (in->p[i], normal);
 +		dot -= dist;
 +		dists[i] = dot;
 +		if (dot > epsilon)
 +			sides[i] = SIDE_FRONT;
 +		else if (dot < -epsilon)
 +			sides[i] = SIDE_BACK;
 +		else
 +		{
 +			sides[i] = SIDE_ON;
 +		}
 +		counts[sides[i]]++;
 +	}
 +	sides[i] = sides[0];
 +	dists[i] = dists[0];
 +	
 +	if (!counts[0])
 +	{
 +		FreeWinding (in);
 +		*inout = NULL;
 +		return;
 +	}
 +	if (!counts[1])
 +		return;		// inout stays the same
 +
 +	maxpts = in->numpoints+4;	// cant use counts[0]+2 because
 +								// of fp grouping errors
 +
 +	f = AllocWinding (maxpts);
 +		
 +	for (i=0 ; i<in->numpoints ; i++)
 +	{
 +		p1 = in->p[i];
 +		
 +		if (sides[i] == SIDE_ON)
 +		{
 +			VectorCopy (p1, f->p[f->numpoints]);
 +			f->numpoints++;
 +			continue;
 +		}
 +	
 +		if (sides[i] == SIDE_FRONT)
 +		{
 +			VectorCopy (p1, f->p[f->numpoints]);
 +			f->numpoints++;
 +		}
 +
 +		if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
 +			continue;
 +			
 +	// generate a split point
 +		p2 = in->p[(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 (normal[j] == 1)
 +				mid[j] = dist;
 +			else if (normal[j] == -1)
 +				mid[j] = -dist;
 +			else
 +				mid[j] = p1[j] + dot*(p2[j]-p1[j]);
 +		}
 +			
 +		VectorCopy (mid, f->p[f->numpoints]);
 +		f->numpoints++;
 +	}
 +	
 +	if (f->numpoints > maxpts)
 +		Error ("ClipWinding: points exceeded estimate");
 +	if (f->numpoints > MAX_POINTS_ON_WINDING)
 +		Error ("ClipWinding: MAX_POINTS_ON_WINDING");
 +
 +	FreeWinding (in);
 +	*inout = f;
 +}
 +
 +
 +/*
 +=================
 +ChopWinding
 +
 +Returns the fragment of in that is on the front side
 +of the cliping plane.  The original is freed.
 +=================
 +*/
 +winding_t	*ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
 +{
 +	winding_t	*f, *b;
 +
 +	ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
 +	FreeWinding (in);
 +	if (b)
 +		FreeWinding (b);
 +	return f;
 +}
 +
 +
 +/*
 +=================
 +CheckWinding
 +
 +=================
 +*/
 +void CheckWinding (winding_t *w)
 +{
 +	int		i, j;
 +	vec_t	*p1, *p2;
 +	vec_t	d, edgedist;
 +	vec3_t	dir, edgenormal, facenormal;
 +	vec_t	area;
 +	vec_t	facedist;
 +
 +	if (w->numpoints < 3)
 +		Error ("CheckWinding: %i points",w->numpoints);
 +	
 +	area = WindingArea(w);
 +	if (area < 1)
 +		Error ("CheckWinding: %f area", area);
 +
 +	WindingPlane (w, facenormal, &facedist);
 +	
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		p1 = w->p[i];
 +
 +		for (j=0 ; j<3 ; j++)
 +			if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
 +				Error ("CheckWinding: BUGUS_RANGE: %f",p1[j]);
 +
 +		j = i+1 == w->numpoints ? 0 : i+1;
 +		
 +	// check the point is on the face plane
 +		d = DotProduct (p1, facenormal) - facedist;
 +		if (d < -ON_EPSILON || d > ON_EPSILON)
 +			Error ("CheckWinding: point off plane");
 +	
 +	// check the edge isnt degenerate
 +		p2 = w->p[j];
 +		VectorSubtract (p2, p1, dir);
 +		
 +		if (VectorLength (dir) < ON_EPSILON)
 +			Error ("CheckWinding: degenerate edge");
 +			
 +		CrossProduct (facenormal, dir, edgenormal);
 +		VectorNormalize (edgenormal);
 +		edgedist = DotProduct (p1, edgenormal);
 +		edgedist += ON_EPSILON;
 +		
 +	// all other points must be on front side
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			if (j == i)
 +				continue;
 +			d = DotProduct (w->p[j], edgenormal);
 +			if (d > edgedist)
 +				Error ("CheckWinding: non-convex");
 +		}
 +	}
 +}
 +
 +
 +/*
 +============
 +WindingOnPlaneSide
 +============
 +*/
 +int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
 +{
 +	qboolean	front, back;
 +	int			i;
 +	vec_t		d;
 +
 +	front = false;
 +	back = false;
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		d = DotProduct (w->p[i], normal) - dist;
 +		if (d < -ON_EPSILON)
 +		{
 +			if (front)
 +				return SIDE_CROSS;
 +			back = true;
 +			continue;
 +		}
 +		if (d > ON_EPSILON)
 +		{
 +			if (back)
 +				return SIDE_CROSS;
 +			front = true;
 +			continue;
 +		}
 +	}
 +
 +	if (back)
 +		return SIDE_BACK;
 +	if (front)
 +		return SIDE_FRONT;
 +	return SIDE_ON;
 +}
 +
 +//#ifdef ME
 +	#define	CONTINUOUS_EPSILON	0.005
 +//#else
 +//	#define	CONTINUOUS_EPSILON	0.001
 +//#endif
 +
 +/*
 +=============
 +TryMergeWinding
 +
 +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.
 +=============
 +*/
 +
 +winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
 +{
 +	vec_t		*p1, *p2, *p3, *p4, *back;
 +	winding_t	*newf;
 +	int			i, j, k, l;
 +	vec3_t		normal, delta;
 +	vec_t		dot;
 +	qboolean	keep1, keep2;
 +	
 +
 +	//
 +	// find a common edge
 +	//	
 +	p1 = p2 = NULL;	// stop compiler warning
 +	j = 0;			// 
 +	
 +	for (i = 0; i < f1->numpoints; i++)
 +	{
 +		p1 = f1->p[i];
 +		p2 = f1->p[(i+1) % f1->numpoints];
 +		for (j = 0; j < f2->numpoints; j++)
 +		{
 +			p3 = f2->p[j];
 +			p4 = f2->p[(j+1) % f2->numpoints];
 +			for (k = 0; k < 3; k++)
 +			{
 +				if (fabs(p1[k] - p4[k]) > 0.1)//EQUAL_EPSILON) //ME
 +					break;
 +				if (fabs(p2[k] - p3[k]) > 0.1)//EQUAL_EPSILON) //ME
 +					break;
 +			} //end for
 +			if (k==3)
 +				break;
 +		} //end for
 +		if (j < f2->numpoints)
 +			break;
 +	} //end for
 +	
 +	if (i == f1->numpoints)
 +		return NULL;			// no matching edges
 +
 +	//
 +	// check slope of connected lines
 +	// if the slopes are colinear, the point can be removed
 +	//
 +	back = f1->p[(i+f1->numpoints-1)%f1->numpoints];
 +	VectorSubtract (p1, back, delta);
 +	CrossProduct (planenormal, delta, normal);
 +	VectorNormalize (normal);
 +	
 +	back = f2->p[(j+2)%f2->numpoints];
 +	VectorSubtract (back, p1, delta);
 +	dot = DotProduct (delta, normal);
 +	if (dot > CONTINUOUS_EPSILON)
 +		return NULL;			// not a convex polygon
 +	keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
 +	
 +	back = f1->p[(i+2)%f1->numpoints];
 +	VectorSubtract (back, p2, delta);
 +	CrossProduct (planenormal, delta, normal);
 +	VectorNormalize (normal);
 +
 +	back = f2->p[(j+f2->numpoints-1)%f2->numpoints];
 +	VectorSubtract (back, p2, delta);
 +	dot = DotProduct (delta, normal);
 +	if (dot > CONTINUOUS_EPSILON)
 +		return NULL;			// not a convex polygon
 +	keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
 +
 +	//
 +	// build the new polygon
 +	//
 +	newf = AllocWinding (f1->numpoints + f2->numpoints);
 +	
 +	// copy first polygon
 +	for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
 +	{
 +		if (k==(i+1)%f1->numpoints && !keep2)
 +			continue;
 +		
 +		VectorCopy (f1->p[k], newf->p[newf->numpoints]);
 +		newf->numpoints++;
 +	}
 +	
 +	// copy second polygon
 +	for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
 +	{
 +		if (l==(j+1)%f2->numpoints && !keep1)
 +			continue;
 +		VectorCopy (f2->p[l], newf->p[newf->numpoints]);
 +		newf->numpoints++;
 +	}
 +
 +	return newf;
 +}
 +
 +//#ifdef ME
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +winding_t *MergeWindings(winding_t *w1, winding_t *w2, vec3_t planenormal)
 +{
 +	winding_t *neww;
 +	float dist;
 +	int i, j, n, found, insertafter;
 +	int sides[MAX_POINTS_ON_WINDING+4];
 +	vec3_t newp[MAX_POINTS_ON_WINDING+4];
 +	int numpoints;
 +	vec3_t edgevec, sepnormal, v;
 +
 +	RemoveEqualPoints(w1, 0.2);
 +	numpoints = w1->numpoints;
 +	memcpy(newp, w1->p, w1->numpoints * sizeof(vec3_t));
 +	//
 +	for (i = 0; i < w2->numpoints; i++)
 +	{
 +		VectorCopy(w2->p[i], v);
 +		for (j = 0; j < numpoints; j++)
 +		{
 +			VectorSubtract(newp[(j+1)%numpoints],
 +							newp[(j)%numpoints], edgevec);
 +			CrossProduct(edgevec, planenormal, sepnormal);
 +			VectorNormalize(sepnormal);
 +			if (VectorLength(sepnormal) < 0.9)
 +			{
 +				//remove the point from the new winding
 +				for (n = j; n < numpoints-1; n++)
 +				{
 +					VectorCopy(newp[n+1], newp[n]);
 +					sides[n] = sides[n+1];
 +				} //end for
 +				numpoints--;
 +				j--;
 +				Log_Print("MergeWindings: degenerate edge on winding %f %f %f\n", sepnormal[0],
 +																				sepnormal[1],
 +																				sepnormal[2]);
 +				continue;
 +			} //end if
 +			dist = DotProduct(newp[(j)%numpoints], sepnormal);
 +			if (DotProduct(v, sepnormal) - dist < -0.1) sides[j] = SIDE_BACK;
 +			else sides[j] = SIDE_FRONT;
 +		} //end for
 +		//remove all unnecesary points
 +		for (j = 0; j < numpoints;)
 +		{
 +			if (sides[j] == SIDE_BACK
 +				&& sides[(j+1)%numpoints] == SIDE_BACK)
 +			{
 +				//remove the point from the new winding
 +				for (n = (j+1)%numpoints; n < numpoints-1; n++)
 +				{
 +					VectorCopy(newp[n+1], newp[n]);
 +					sides[n] = sides[n+1];
 +				} //end for
 +				numpoints--;
 +			} //end if
 +			else
 +			{
 +				j++;
 +			} //end else
 +		} //end for
 +		//
 +		found = false;
 +		for (j = 0; j < numpoints; j++)
 +		{
 +			if (sides[j] == SIDE_FRONT
 +				&& sides[(j+1)%numpoints] == SIDE_BACK)
 +			{
 +				if (found) Log_Print("Warning: MergeWindings: front to back found twice\n");
 +				found = true;
 +			} //end if
 +		} //end for
 +		//
 +		for (j = 0; j < numpoints; j++)
 +		{
 +			if (sides[j] == SIDE_FRONT
 +				&& sides[(j+1)%numpoints] == SIDE_BACK)
 +			{
 +				insertafter = (j+1)%numpoints;
 +				//insert the new point after j+1
 +				for (n = numpoints-1; n > insertafter; n--)
 +				{
 +					VectorCopy(newp[n], newp[n+1]);
 +				} //end for
 +				numpoints++;
 +				VectorCopy(v, newp[(insertafter+1)%numpoints]);
 +				break;
 +			} //end if
 +		} //end for
 +	} //end for
 +	neww = AllocWinding(numpoints);
 +	neww->numpoints = numpoints;
 +	memcpy(neww->p, newp, numpoints * sizeof(vec3_t));
 +	RemoveColinearPoints(neww);
 +	return neww;
 +} //end of the function MergeWindings
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +char *WindingErrorString(void)
 +{
 +	return windingerror;
 +} //end of the function WindingErrorString
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int WindingError(winding_t *w)
 +{
 +	int		i, j;
 +	vec_t	*p1, *p2;
 +	vec_t	d, edgedist;
 +	vec3_t	dir, edgenormal, facenormal;
 +	vec_t	area;
 +	vec_t	facedist;
 +
 +	if (w->numpoints < 3)
 +	{
 +		sprintf(windingerror, "winding %i points", w->numpoints);
 +		return WE_NOTENOUGHPOINTS;
 +	} //end if
 +	
 +	area = WindingArea(w);
 +	if (area < 1)
 +	{
 +		sprintf(windingerror, "winding %f area", area);
 +		return WE_SMALLAREA;
 +	} //end if
 +
 +	WindingPlane (w, facenormal, &facedist);
 +	
 +	for (i=0 ; i<w->numpoints ; i++)
 +	{
 +		p1 = w->p[i];
 +
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
 +			{
 +				sprintf(windingerror, "winding point %d BUGUS_RANGE \'%f %f %f\'", j, p1[0], p1[1], p1[2]);
 +				return WE_POINTBOGUSRANGE;
 +			} //end if
 +		} //end for
 +
 +		j = i+1 == w->numpoints ? 0 : i+1;
 +		
 +	// check the point is on the face plane
 +		d = DotProduct (p1, facenormal) - facedist;
 +		if (d < -ON_EPSILON || d > ON_EPSILON)
 +		{
 +			sprintf(windingerror, "winding point %d off plane", i);
 +			return WE_POINTOFFPLANE;
 +		} //end if
 +	
 +	// check the edge isnt degenerate
 +		p2 = w->p[j];
 +		VectorSubtract (p2, p1, dir);
 +		
 +		if (VectorLength (dir) < ON_EPSILON)
 +		{
 +			sprintf(windingerror, "winding degenerate edge %d-%d", i, j);
 +			return WE_DEGENERATEEDGE;
 +		} //end if
 +			
 +		CrossProduct (facenormal, dir, edgenormal);
 +		VectorNormalize (edgenormal);
 +		edgedist = DotProduct (p1, edgenormal);
 +		edgedist += ON_EPSILON;
 +		
 +	// all other points must be on front side
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			if (j == i)
 +				continue;
 +			d = DotProduct (w->p[j], edgenormal);
 +			if (d > edgedist)
 +			{
 +				sprintf(windingerror, "winding non-convex");
 +				return WE_NONCONVEX;
 +			} //end if
 +		} //end for
 +	} //end for
 +	return WE_NONE;
 +} //end of the function WindingError
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemoveEqualPoints(winding_t *w, float epsilon)
 +{
 +	int i, nump;
 +	vec3_t v;
 +	vec3_t p[MAX_POINTS_ON_WINDING];
 +
 +	VectorCopy(w->p[0], p[0]);
 +	nump = 1;
 +	for (i = 1; i < w->numpoints; i++)
 +	{
 +		VectorSubtract(w->p[i], p[nump-1], v);
 +		if (VectorLength(v) > epsilon)
 +		{
 +			if (nump >= MAX_POINTS_ON_WINDING)
 +				Error("RemoveColinearPoints: MAX_POINTS_ON_WINDING");
 +			VectorCopy (w->p[i], p[nump]);
 +			nump++;
 +		} //end if
 +	} //end for
 +
 +	if (nump == w->numpoints)
 +		return;
 +
 +	w->numpoints = nump;
 +	memcpy(w->p, p, nump * sizeof(p[0]));
 +} //end of the function RemoveEqualPoints
 +//===========================================================================
 +// adds the given point to a winding at the given spot
 +// (for instance when spot is zero then the point is added at position zero)
 +// the original winding is NOT freed
 +//
 +// Parameter:				-
 +// Returns:					the new winding with the added point
 +// Changes Globals:		-
 +//===========================================================================
 +winding_t *AddWindingPoint(winding_t *w, vec3_t point, int spot)
 +{
 +	int i, j;
 +	winding_t *neww;
 +
 +	if (spot > w->numpoints)
 +	{
 +		Error("AddWindingPoint: num > w->numpoints");
 +	} //end if
 +	if (spot < 0)
 +	{
 +		Error("AddWindingPoint: num < 0");
 +	} //end if
 +	neww = AllocWinding(w->numpoints + 1);
 +	neww->numpoints = w->numpoints + 1;
 +	for (i = 0, j = 0; i < neww->numpoints; i++)
 +	{
 +		if (i == spot)
 +		{
 +			VectorCopy(point, neww->p[i]);
 +		} //end if
 +		else
 +		{
 +			VectorCopy(w->p[j], neww->p[i]);
 +			j++;
 +		} //end else
 +	} //end for
 +	return neww;
 +} //end of the function AddWindingPoint
 +//===========================================================================
 +// the position where the new point should be added in the winding is
 +// stored in *spot
 +//
 +// Parameter:				-
 +// Returns:					true if the point is on the winding
 +// Changes Globals:		-
 +//===========================================================================
 +#define MELT_ON_EPSILON		0.2
 +
 +int PointOnWinding(winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot)
 +{
 +	int i, j;
 +	vec3_t v1, v2;
 +	vec3_t edgenormal, edgevec;
 +	float edgedist, dot;
 +
 +	*spot = 0;
 +	//the point must be on the winding plane
 +	dot = DotProduct(point, normal) - dist;
 +	if (dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON) return false;
 +	//
 +	for (i = 0; i < w->numpoints; i++)
 +	{
 +		j = (i+1) % w->numpoints;
 +		//get a plane orthogonal to the winding plane through the edge
 +		VectorSubtract(w->p[j], w->p[i], edgevec);
 +		CrossProduct(normal, edgevec, edgenormal);
 +		VectorNormalize(edgenormal);
 +		edgedist = DotProduct(edgenormal, w->p[i]);
 +		//point must be not too far from the plane
 +		dot = DotProduct(point, edgenormal) - edgedist;
 +		if (dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON) continue;
 +		//vector from first point of winding to the point to test
 +		VectorSubtract(point, w->p[i], v1);
 +		//vector from second point of winding to the point to test
 +		VectorSubtract(point, w->p[j], v2);
 +		//if the length of the vector is not larger than 0.5 units then
 +		//the point is assumend to be the same as one of the winding points
 +		if (VectorNormalize(v1) < 0.5) return false;
 +		if (VectorNormalize(v2) < 0.5) return false;
 +		//point must be between the two winding points
 +		//(the two vectors must be directed towards each other, and on the
 +		//same straight line)
 +		if (DotProduct(v1, v2) < -0.99)
 +		{
 +			*spot = i + 1;
 +			return true;
 +		} //end if
 +	} //end for
 +	return false;
 +} //end of the function PointOnWinding
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int FindPlaneSeperatingWindings(winding_t *w1, winding_t *w2, vec3_t dir,
 +											vec3_t normal, float *dist)
 +{
 +	int i, i2, j, j2, n;
 +	int sides1[3], sides2[3];
 +	float dist1, dist2, dot, diff;
 +	vec3_t normal1, normal2;
 +	vec3_t v1, v2;
 +
 +	for (i = 0; i < w1->numpoints; i++)
 +	{
 +		i2 = (i+1) % w1->numpoints;
 +		//
 +		VectorSubtract(w1->p[i2], w1->p[i], v1);
 +		if (VectorLength(v1) < 0.1)
 +		{
 +			//Log_Write("FindPlaneSeperatingWindings: winding1 with degenerate edge\r\n");
 +			continue;
 +		} //end if
 +		CrossProduct(v1, dir, normal1);
 +		VectorNormalize(normal1);
 +		dist1 = DotProduct(normal1, w1->p[i]);
 +		//
 +		for (j = 0; j < w2->numpoints; j++)
 +		{
 +			j2 = (j+1) % w2->numpoints;
 +			//
 +			VectorSubtract(w2->p[j2], w2->p[j], v2);
 +			if (VectorLength(v2) < 0.1)
 +			{
 +				//Log_Write("FindPlaneSeperatingWindings: winding2 with degenerate edge\r\n");
 +				continue;
 +			} //end if
 +			CrossProduct(v2, dir, normal2);
 +			VectorNormalize(normal2);
 +			dist2 = DotProduct(normal2, w2->p[j]);
 +			//
 +			diff = dist1 - dist2;
 +			if (diff < -0.1 || diff > 0.1)
 +			{
 +				dist2 = -dist2;
 +				VectorNegate(normal2, normal2);
 +				diff = dist1 - dist2;
 +				if (diff < -0.1 || diff > 0.1) continue;
 +			} //end if
 +			//check if the normal vectors are equal
 +			for (n = 0; n < 3; n++)
 +			{
 +				diff = normal1[n] - normal2[n];
 +				if (diff < -0.0001 || diff > 0.0001) break;
 +			} //end for
 +			if (n != 3) continue;
 +			//check on which side of the seperating plane the points of
 +			//the first winding are
 +			sides1[0] = sides1[1] = sides1[2] = 0;
 +			for (n = 0; n < w1->numpoints; n++)
 +			{
 +				dot = DotProduct(w1->p[n], normal1) - dist1;
 +				if (dot > 0.1) sides1[0]++;
 +				else if (dot < -0.1) sides1[1]++;
 +				else sides1[2]++;
 +			} //end for
 +			//check on which side of the seperating plane the points of
 +			//the second winding are
 +			sides2[0] = sides2[1] = sides2[2] = 0;
 +			for (n = 0; n < w2->numpoints; n++)
 +			{
 +				//used normal1 and dist1 (they are equal to normal2 and dist2)
 +				dot = DotProduct(w2->p[n], normal1) - dist1;
 +				if (dot > 0.1) sides2[0]++;
 +				else if (dot < -0.1) sides2[1]++;
 +				else sides2[2]++;
 +			} //end for
 +			//if the first winding has points at both sides
 +			if (sides1[0] && sides1[1])
 +			{
 +				Log_Write("FindPlaneSeperatingWindings: winding1 non-convex\r\n");
 +				continue;
 +			} //end if
 +			//if the second winding has points at both sides
 +			if (sides2[0] && sides2[1])
 +			{
 +				Log_Write("FindPlaneSeperatingWindings: winding2 non-convex\r\n");
 +				continue;
 +			} //end if
 +			//
 +			if ((!sides1[0] && !sides1[1]) || (!sides2[0] && !sides2[1]))
 +			{
 +				//don't use one of the winding planes as the seperating plane
 +				continue;
 +			} //end if
 +			//the windings must be at different sides of the seperating plane
 +			if ((!sides1[0] && !sides2[1]) || (!sides1[1] && !sides2[0]))
 +			{
 +				VectorCopy(normal1, normal);
 +				*dist = dist1;
 +				return true;
 +			} //end if
 +		} //end for
 +	} //end for
 +	return false;
 +} //end of the function FindPlaneSeperatingWindings
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#define WCONVEX_EPSILON		0.2
 +
 +int WindingsNonConvex(winding_t *w1, winding_t *w2,
 +							 vec3_t normal1, vec3_t normal2,
 +							 float dist1, float dist2)
 +{
 +	int i;
 +
 +	if (!w1 || !w2) return false;
 +
 +	//check if one of the points of face1 is at the back of the plane of face2
 +	for (i = 0; i < w1->numpoints; i++)
 +	{
 +		if (DotProduct(normal2, w1->p[i]) - dist2 > WCONVEX_EPSILON) return true;
 +	} //end for
 +	//check if one of the points of face2 is at the back of the plane of face1
 +	for (i = 0; i < w2->numpoints; i++)
 +	{
 +		if (DotProduct(normal1, w2->p[i]) - dist1 > WCONVEX_EPSILON) return true;
 +	} //end for
 +
 +	return false;
 +} //end of the function WindingsNonConvex
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +/*
 +#define VERTEX_EPSILON		0.5
 +
 +qboolean EqualVertexes(vec3_t v1, vec3_t v2)
 +{
 +	float diff;
 +
 +	diff = v1[0] - v2[0];
 +	if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON)
 +	{
 +		diff = v1[1] - v2[1];
 +		if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON)
 +		{
 +			diff = v1[2] - v2[2];
 +			if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON)
 +			{
 +				return true;
 +			} //end if
 +		} //end if
 +	} //end if
 +	return false;
 +} //end of the function EqualVertexes
 +
 +#define	CONTINUOUS_EPSILON	0.001
 +
 +winding_t *AAS_MergeWindings(winding_t *w1, winding_t *w2, vec3_t windingnormal)
 +{
 +	int n, i, k;
 +	vec3_t normal, delta;
 +	winding_t *winding, *neww;
 +	float dist, dot;
 +	int p1, p2;
 +	int points[2][64];
 +	int numpoints[2] = {0, 0};
 +	int newnumpoints;
 +	int keep[2];
 +
 +	if (!FindPlaneSeperatingWindings(w1, w2, windingnormal, normal, &dist)) return NULL;
 +
 +	//for both windings
 +	for (n = 0; n < 2; n++)
 +	{
 +		if (n == 0) winding = w1;
 +		else winding = w2;
 +		//get the points of the winding which are on the seperating plane
 +		for (i = 0; i < winding->numpoints; i++)
 +		{
 +			dot = DotProduct(winding->p[i], normal) - dist;
 +			if (dot > -ON_EPSILON && dot < ON_EPSILON)
 +			{
 +				//don't allow more than 64 points on the seperating plane
 +				if (numpoints[n] >= 64) Error("AAS_MergeWindings: more than 64 points on seperating plane\n");
 +				points[n][numpoints[n]++] = i;
 +			} //end if
 +		} //end for
 +		//there must be at least two points of each winding on the seperating plane
 +		if (numpoints[n] < 2) return NULL;
 +	} //end for
 +
 +	//if the first point of winding1 (which is on the seperating plane) is unequal
 +	//to the last point of winding2 (which is on the seperating plane)
 +	if (!EqualVertexes(w1->p[points[0][0]], w2->p[points[1][numpoints[1]-1]]))
 +	{
 +		return NULL;
 +	} //end if
 +	//if the last point of winding1 (which is on the seperating plane) is unequal
 +	//to the first point of winding2 (which is on the seperating plane)
 +	if (!EqualVertexes(w1->p[points[0][numpoints[0]-1]], w2->p[points[1][0]]))
 +	{
 +		return NULL;
 +	} //end if
 +	//
 +	// check slope of connected lines
 +	// if the slopes are colinear, the point can be removed
 +	//
 +	//first point of winding1 which is on the seperating plane
 +	p1 = points[0][0];
 +	//point before p1
 +	p2 = (p1 + w1->numpoints - 1) % w1->numpoints;
 +	VectorSubtract(w1->p[p1], w1->p[p2], delta);
 +	CrossProduct(windingnormal, delta, normal);
 +	VectorNormalize(normal, normal);
 +
 +	//last point of winding2 which is on the seperating plane
 +	p1 = points[1][numpoints[1]-1];
 +	//point after p1
 +	p2 = (p1 + 1) % w2->numpoints;
 +	VectorSubtract(w2->p[p2], w2->p[p1], delta);
 +	dot = DotProduct(delta, normal);
 +	if (dot > CONTINUOUS_EPSILON) return NULL; //merging would create a non-convex polygon
 +	keep[0] = (qboolean)(dot < -CONTINUOUS_EPSILON);
 +
 +	//first point of winding2 which is on the seperating plane
 +	p1 = points[1][0];
 +	//point before p1
 +	p2 = (p1 + w2->numpoints - 1) % w2->numpoints;
 +	VectorSubtract(w2->p[p1], w2->p[p2], delta);
 +	CrossProduct(windingnormal, delta, normal);
 +	VectorNormalize(normal, normal);
 +
 +	//last point of winding1 which is on the seperating plane
 +	p1 = points[0][numpoints[0]-1];
 +	//point after p1
 +	p2 = (p1 + 1) % w1->numpoints;
 +	VectorSubtract(w1->p[p2], w1->p[p1], delta);
 +	dot = DotProduct(delta, normal);
 +	if (dot > CONTINUOUS_EPSILON) return NULL; //merging would create a non-convex polygon
 +	keep[1] = (qboolean)(dot < -CONTINUOUS_EPSILON);
 +
 +	//number of points on the new winding
 +	newnumpoints = w1->numpoints - numpoints[0] + w2->numpoints - numpoints[1] + 2;
 +	//allocate the winding
 +	neww = AllocWinding(newnumpoints);
 +	neww->numpoints = newnumpoints;
 +	//copy all the points
 +	k = 0;
 +	//for both windings
 +	for (n = 0; n < 2; n++)
 +	{
 +		if (n == 0) winding = w1;
 +		else winding = w2;
 +		//copy the points of the winding starting with the last point on the
 +		//seperating plane and ending before the first point on the seperating plane
 +		for (i = points[n][numpoints[n]-1]; i != points[n][0]; i = (i+1)%winding->numpoints)
 +		{
 +			if (k >= newnumpoints)
 +			{
 +				Log_Print("numpoints[0] = %d\n", numpoints[0]);
 +				Log_Print("numpoints[1] = %d\n", numpoints[1]);
 +				Error("AAS_MergeWindings: k = %d >= newnumpoints = %d\n", k, newnumpoints);
 +			} //end if
 +			VectorCopy(winding->p[i], neww->p[k]);
 +			k++;
 +		} //end for
 +	} //end for
 +	RemoveEqualPoints(neww);
 +	if (!WindingIsOk(neww, 1))
 +	{
 +		Log_Print("AAS_MergeWindings: winding not ok after merging\n");
 +		FreeWinding(neww);
 +		return NULL;
 +	} //end if
 +	return neww;
 +} //end of the function AAS_MergeWindings*/
 +//#endif //ME
 diff --git a/code/bspc/l_poly.h b/code/bspc/l_poly.h new file mode 100755 index 0000000..a14b834 --- /dev/null +++ b/code/bspc/l_poly.h @@ -0,0 +1,120 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//a winding gives the bounding points of a convex polygon
 +typedef struct
 +{
 +	int		numpoints;
 +	vec3_t	p[4];			//variable sized
 +} winding_t;
 +
 +#define	MAX_POINTS_ON_WINDING	96
 +
 +//you can define on_epsilon in the makefile as tighter
 +#ifndef	ON_EPSILON
 +#define	ON_EPSILON	0.1
 +#endif
 +//winding errors
 +#define WE_NONE						0
 +#define WE_NOTENOUGHPOINTS			1
 +#define WE_SMALLAREA					2
 +#define WE_POINTBOGUSRANGE			3
 +#define WE_POINTOFFPLANE			4
 +#define WE_DEGENERATEEDGE			5
 +#define WE_NONCONVEX					6
 +
 +//allocates a winding
 +winding_t *AllocWinding (int points);
 +//returns the area of the winding
 +vec_t WindingArea (winding_t *w);
 +//gives the center of the winding
 +void WindingCenter (winding_t *w, vec3_t center);
 +//clips the given winding to the given plane and gives the front
 +//and back part of the clipped winding
 +void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, 
 +					vec_t epsilon, winding_t **front, winding_t **back);
 +//returns the fragment of the given winding that is on the front
 +//side of the cliping plane. The original is freed.
 +winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
 +//returns a copy of the given winding
 +winding_t *CopyWinding (winding_t *w);
 +//returns the reversed winding of the given one
 +winding_t *ReverseWinding (winding_t *w);
 +//returns a base winding for the given plane
 +winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
 +//checks the winding for errors
 +void CheckWinding (winding_t *w);
 +//returns the plane normal and dist the winding is in
 +void WindingPlane(winding_t *w, vec3_t normal, vec_t *dist);
 +//removes colinear points from the winding
 +void RemoveColinearPoints(winding_t *w);
 +//returns on which side of the plane the winding is situated
 +int WindingOnPlaneSide(winding_t *w, vec3_t normal, vec_t dist);
 +//frees the winding
 +void FreeWinding(winding_t *w);
 +//gets the bounds of the winding
 +void WindingBounds(winding_t *w, vec3_t mins, vec3_t maxs);
 +//chops the winding with the given plane, the original winding is freed if clipped
 +void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
 +//prints the winding points on STDOUT
 +void pw(winding_t *w);
 +//try to merge the two windings which are in the given plane
 +//the original windings are undisturbed
 +//the merged winding is returned when merging was possible
 +//NULL is returned otherwise
 +winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal);
 +//brute force winding merging... creates a convex winding out of
 +//the two whatsoever
 +winding_t *MergeWindings(winding_t *w1, winding_t *w2, vec3_t planenormal);
 +
 +//#ifdef ME
 +void ResetWindings(void);
 +//returns the amount of winding memory
 +int WindingMemory(void);
 +int WindingPeakMemory(void);
 +int ActiveWindings(void);
 +//returns the winding error string
 +char *WindingErrorString(void);
 +//returns one of the WE_ flags when the winding has errors
 +int WindingError(winding_t *w);
 +//removes equal points from the winding
 +void RemoveEqualPoints(winding_t *w, float epsilon);
 +//returns a winding with a point added at the given spot to the
 +//given winding, original winding is NOT freed
 +winding_t *AddWindingPoint(winding_t *w, vec3_t point, int spot);
 +//returns true if the point is on one of the winding 'edges'
 +//when the point is on one of the edged the number of the first
 +//point of the edge is stored in 'spot' 
 +int PointOnWinding(winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot);
 +//find a plane seperating the two windings
 +//true is returned when the windings area adjacent
 +//the seperating plane normal and distance area stored in 'normal' and 'dist'
 +//this plane will contain both the piece of common edge of the two windings
 +//and the vector 'dir'
 +int FindPlaneSeperatingWindings(winding_t *w1, winding_t *w2, vec3_t dir,
 +											vec3_t normal, float *dist);
 +//
 +int WindingsNonConvex(winding_t *w1, winding_t *w2,
 +							 vec3_t normal1, vec3_t normal2,
 +							 float dist1, float dist2);
 +//#endif //ME
 +
 diff --git a/code/bspc/l_qfiles.c b/code/bspc/l_qfiles.c new file mode 100755 index 0000000..32cb6de --- /dev/null +++ b/code/bspc/l_qfiles.c @@ -0,0 +1,663 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#if defined(WIN32)|defined(_WIN32)
 +#include <windows.h>
 +#include <sys/types.h>
 +#include <sys/stat.h>
 +#else
 +#include <glob.h>
 +#include <sys/stat.h>
 +#include <unistd.h>
 +#endif
 +
 +#include "qbsp.h"
 +
 +//file extensions with their type
 +typedef struct qfile_exttype_s
 +{
 +	char *extension;
 +	int type;
 +} qfile_exttyp_t;
 +
 +qfile_exttyp_t quakefiletypes[] =
 +{
 +	{QFILEEXT_UNKNOWN, QFILETYPE_UNKNOWN},
 +	{QFILEEXT_PAK, QFILETYPE_PAK},
 +	{QFILEEXT_PK3, QFILETYPE_PK3},
 +	{QFILEEXT_SIN, QFILETYPE_PAK},
 +	{QFILEEXT_BSP, QFILETYPE_BSP},
 +	{QFILEEXT_MAP, QFILETYPE_MAP},
 +	{QFILEEXT_MDL, QFILETYPE_MDL},
 +	{QFILEEXT_MD2, QFILETYPE_MD2},
 +	{QFILEEXT_MD3, QFILETYPE_MD3},
 +	{QFILEEXT_WAL, QFILETYPE_WAL},
 +	{QFILEEXT_WAV, QFILETYPE_WAV},
 +	{QFILEEXT_AAS, QFILETYPE_AAS},
 +	{NULL, 0}
 +};
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int QuakeFileExtensionType(char *extension)
 +{
 +	int i;
 +
 +	for (i = 0; quakefiletypes[i].extension; i++)
 +	{
 +		if (!stricmp(extension, quakefiletypes[i].extension))
 +		{
 +			return quakefiletypes[i].type;
 +		} //end if
 +	} //end for
 +	return QFILETYPE_UNKNOWN;
 +} //end of the function QuakeFileExtensionType
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +char *QuakeFileTypeExtension(int type)
 +{
 +	int i;
 +
 +	for (i = 0; quakefiletypes[i].extension; i++)
 +	{
 +		if (quakefiletypes[i].type == type)
 +		{
 +			return quakefiletypes[i].extension;
 +		} //end if
 +	} //end for
 +	return QFILEEXT_UNKNOWN;
 +} //end of the function QuakeFileExtension
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int QuakeFileType(char *filename)
 +{
 +	char ext[_MAX_PATH] = ".";
 +
 +	ExtractFileExtension(filename, ext+1);
 +	return QuakeFileExtensionType(ext);
 +} //end of the function QuakeFileTypeFromFileName
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +char *StringContains(char *str1, char *str2, int casesensitive)
 +{
 +	int len, i, j;
 +
 +	len = strlen(str1) - strlen(str2);
 +	for (i = 0; i <= len; i++, str1++)
 +	{
 +		for (j = 0; str2[j]; j++)
 +		{
 +			if (casesensitive)
 +			{
 +				if (str1[j] != str2[j]) break;
 +			} //end if
 +			else
 +			{
 +				if (toupper(str1[j]) != toupper(str2[j])) break;
 +			} //end else
 +		} //end for
 +		if (!str2[j]) return str1;
 +	} //end for
 +	return NULL;
 +} //end of the function StringContains
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int FileFilter(char *filter, char *filename, int casesensitive)
 +{
 +	char buf[1024];
 +	char *ptr;
 +	int i, found;
 +
 +	while(*filter)
 +	{
 +		if (*filter == '*')
 +		{
 +			filter++;
 +			for (i = 0; *filter; i++)
 +			{
 +				if (*filter == '*' || *filter == '?') break;
 +				buf[i] = *filter;
 +				filter++;
 +			} //end for
 +			buf[i] = '\0';
 +			if (strlen(buf))
 +			{
 +				ptr = StringContains(filename, buf, casesensitive);
 +				if (!ptr) return false;
 +				filename = ptr + strlen(buf);
 +			} //end if
 +		} //end if
 +		else if (*filter == '?')
 +		{
 +			filter++;
 +			filename++;
 +		} //end else if
 +		else if (*filter == '[' && *(filter+1) == '[')
 +		{
 +			filter++;
 +		} //end if
 +		else if (*filter == '[')
 +		{
 +			filter++;
 +			found = false;
 +			while(*filter && !found)
 +			{
 +				if (*filter == ']' && *(filter+1) != ']') break;
 +				if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']'))
 +				{
 +					if (casesensitive)
 +					{
 +						if (*filename >= *filter && *filename <= *(filter+2)) found = true;
 +					} //end if
 +					else
 +					{
 +						if (toupper(*filename) >= toupper(*filter) &&
 +							toupper(*filename) <= toupper(*(filter+2))) found = true;
 +					} //end else
 +					filter += 3;
 +				} //end if
 +				else
 +				{
 +					if (casesensitive)
 +					{
 +						if (*filter == *filename) found = true;
 +					} //end if
 +					else
 +					{
 +						if (toupper(*filter) == toupper(*filename)) found = true;
 +					} //end else
 +					filter++;
 +				} //end else
 +			} //end while
 +			if (!found) return false;
 +			while(*filter)
 +			{
 +				if (*filter == ']' && *(filter+1) != ']') break;
 +				filter++;
 +			} //end while
 +			filter++;
 +			filename++;
 +		} //end else if
 +		else
 +		{
 +			if (casesensitive)
 +			{
 +				if (*filter != *filename) return false;
 +			} //end if
 +			else
 +			{
 +				if (toupper(*filter) != toupper(*filename)) return false;
 +			} //end else
 +			filter++;
 +			filename++;
 +		} //end else
 +	} //end while
 +	return true;
 +} //end of the function FileFilter
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +quakefile_t *FindQuakeFilesInZip(char *zipfile, char *filter)
 +{
 +	unzFile			uf;
 +	int				err;
 +	unz_global_info gi;
 +	char			filename_inzip[MAX_PATH];
 +	unz_file_info	file_info;
 +	int				i;
 +	quakefile_t		*qfiles, *lastqf, *qf;
 +
 +	uf = unzOpen(zipfile);
 +	err = unzGetGlobalInfo(uf, &gi);
 +
 +	if (err != UNZ_OK) return NULL;
 +
 +	unzGoToFirstFile(uf);
 +
 +	qfiles = NULL;
 +	lastqf = NULL;
 +	for (i = 0; i < gi.number_entry; i++)
 +	{
 +		err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL,0,NULL,0);
 +		if (err != UNZ_OK) break;
 +
 +		ConvertPath(filename_inzip);
 +		if (FileFilter(filter, filename_inzip, false))
 +		{
 +			qf = malloc(sizeof(quakefile_t));
 +			if (!qf) Error("out of memory");
 +			memset(qf, 0, sizeof(quakefile_t));
 +			strcpy(qf->pakfile, zipfile);
 +			strcpy(qf->filename, zipfile);
 +			strcpy(qf->origname, filename_inzip);
 +			qf->zipfile = true;
 +			//memcpy( &buildBuffer[i].zipfileinfo, (unz_s*)uf, sizeof(unz_s));
 +			memcpy(&qf->zipinfo, (unz_s*)uf, sizeof(unz_s));
 +			qf->offset = 0;
 +			qf->length = file_info.uncompressed_size;
 +			qf->type = QuakeFileType(filename_inzip);
 +			//add the file ot the list
 +			qf->next = NULL;
 +			if (lastqf) lastqf->next = qf;
 +			else qfiles = qf;
 +			lastqf = qf;
 +		} //end if
 +		unzGoToNextFile(uf);
 +	} //end for
 +
 +	unzClose(uf);
 +
 +	return qfiles;
 +} //end of the function FindQuakeFilesInZip
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +quakefile_t *FindQuakeFilesInPak(char *pakfile, char *filter)
 +{
 +	FILE *fp;
 +	dpackheader_t packheader;
 +	dsinpackfile_t *packfiles;
 +	dpackfile_t *idpackfiles;
 +	quakefile_t *qfiles, *lastqf, *qf;
 +	int numpackdirs, i;
 +
 +	qfiles = NULL;
 +	lastqf = NULL;
 +	//open the pak file
 +	fp = fopen(pakfile, "rb");
 +	if (!fp)
 +	{
 +		Warning("can't open pak file %s", pakfile);
 +		return NULL;
 +	} //end if
 +	//read pak header, check for valid pak id and seek to the dir entries
 +	if ((fread(&packheader, 1, sizeof(dpackheader_t), fp) != sizeof(dpackheader_t))
 +		|| (packheader.ident != IDPAKHEADER && packheader.ident != SINPAKHEADER)
 +		||	(fseek(fp, LittleLong(packheader.dirofs), SEEK_SET))
 +		)
 +	{
 +		fclose(fp);
 +		Warning("invalid pak file %s", pakfile);
 +		return NULL;
 +	} //end if
 +	//if it is a pak file from id software
 +	if (packheader.ident == IDPAKHEADER)
 +	{
 +		//number of dir entries in the pak file
 +		numpackdirs = LittleLong(packheader.dirlen) / sizeof(dpackfile_t);
 +		idpackfiles = (dpackfile_t *) malloc(numpackdirs * sizeof(dpackfile_t));
 +		if (!idpackfiles) Error("out of memory");
 +		//read the dir entry
 +		if (fread(idpackfiles, sizeof(dpackfile_t), numpackdirs, fp) != numpackdirs)
 +		{
 +			fclose(fp);
 +			free(idpackfiles);
 +			Warning("can't read the Quake pak file dir entries from %s", pakfile);
 +			return NULL;
 +		} //end if
 +		fclose(fp);
 +		//convert to sin pack files
 +		packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t));
 +		if (!packfiles) Error("out of memory");
 +		for (i = 0; i < numpackdirs; i++)
 +		{
 +			strcpy(packfiles[i].name, idpackfiles[i].name);
 +			packfiles[i].filepos = LittleLong(idpackfiles[i].filepos);
 +			packfiles[i].filelen = LittleLong(idpackfiles[i].filelen);
 +		} //end for
 +		free(idpackfiles);
 +	} //end if
 +	else //its a Sin pack file
 +	{
 +		//number of dir entries in the pak file
 +		numpackdirs = LittleLong(packheader.dirlen) / sizeof(dsinpackfile_t);
 +		packfiles = (dsinpackfile_t *) malloc(numpackdirs * sizeof(dsinpackfile_t));
 +		if (!packfiles) Error("out of memory");
 +		//read the dir entry
 +		if (fread(packfiles, sizeof(dsinpackfile_t), numpackdirs, fp) != numpackdirs)
 +		{
 +			fclose(fp);
 +			free(packfiles);
 +			Warning("can't read the Sin pak file dir entries from %s", pakfile);
 +			return NULL;
 +		} //end if
 +		fclose(fp);
 +		for (i = 0; i < numpackdirs; i++)
 +		{
 +			packfiles[i].filepos = LittleLong(packfiles[i].filepos);
 +			packfiles[i].filelen = LittleLong(packfiles[i].filelen);
 +		} //end for
 +	} //end else
 +	//
 +	for (i = 0; i < numpackdirs; i++)
 +	{
 +		ConvertPath(packfiles[i].name);
 +		if (FileFilter(filter, packfiles[i].name, false))
 +		{
 +			qf = malloc(sizeof(quakefile_t));
 +			if (!qf) Error("out of memory");
 +			memset(qf, 0, sizeof(quakefile_t));
 +			strcpy(qf->pakfile, pakfile);
 +			strcpy(qf->filename, pakfile);
 +			strcpy(qf->origname, packfiles[i].name);
 +			qf->zipfile = false;
 +			qf->offset = packfiles[i].filepos;
 +			qf->length = packfiles[i].filelen;
 +			qf->type = QuakeFileType(packfiles[i].name);
 +			//add the file ot the list
 +			qf->next = NULL;
 +			if (lastqf) lastqf->next = qf;
 +			else qfiles = qf;
 +			lastqf = qf;
 +		} //end if
 +	} //end for
 +	free(packfiles);
 +	return qfiles;
 +} //end of the function FindQuakeFilesInPak
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +quakefile_t *FindQuakeFilesWithPakFilter(char *pakfilter, char *filter)
 +{
 +#if defined(WIN32)|defined(_WIN32)
 +	WIN32_FIND_DATA filedata;
 +	HWND handle;
 +	struct _stat statbuf;
 +#else
 +	glob_t globbuf;
 +	struct stat statbuf;
 +	int j;
 +#endif
 +	quakefile_t *qfiles, *lastqf, *qf;
 +	char pakfile[_MAX_PATH], filename[_MAX_PATH], *str;
 +	int done;
 +
 +	qfiles = NULL;
 +	lastqf = NULL;
 +	if (pakfilter && strlen(pakfilter))
 +	{
 +#if defined(WIN32)|defined(_WIN32)
 +		handle = FindFirstFile(pakfilter, &filedata);
 +		done = (handle == INVALID_HANDLE_VALUE);
 +		while(!done)
 +		{
 +			_splitpath(pakfilter, pakfile, NULL, NULL, NULL);
 +			_splitpath(pakfilter, NULL, &pakfile[strlen(pakfile)], NULL, NULL);
 +			AppendPathSeperator(pakfile, _MAX_PATH);
 +			strcat(pakfile, filedata.cFileName);
 +			_stat(pakfile, &statbuf);
 +#else
 +		glob(pakfilter, 0, NULL, &globbuf);
 +		for (j = 0; j < globbuf.gl_pathc; j++)
 +		{
 +			strcpy(pakfile, globbuf.gl_pathv[j]);
 +			stat(pakfile, &statbuf);
 +#endif
 +			//if the file with .pak or .pk3 is a folder
 +			if (statbuf.st_mode & S_IFDIR)
 +			{
 +				strcpy(filename, pakfilter);
 +				AppendPathSeperator(filename, _MAX_PATH);
 +				strcat(filename, filter);
 +				qf = FindQuakeFilesWithPakFilter(NULL, filename);
 +				if (lastqf) lastqf->next = qf;
 +				else qfiles = qf;
 +				lastqf = qf;
 +				while(lastqf->next) lastqf = lastqf->next;
 +			} //end if
 +			else
 +			{
 +#if defined(WIN32)|defined(_WIN32)
 +				str = StringContains(pakfile, ".pk3", false);
 +#else
 +				str = StringContains(pakfile, ".pk3", true);
 +#endif
 +				if (str && str == pakfile + strlen(pakfile) - strlen(".pk3"))
 +				{
 +					qf = FindQuakeFilesInZip(pakfile, filter);
 +				} //end if
 +				else
 +				{
 +					qf = FindQuakeFilesInPak(pakfile, filter);
 +				} //end else
 +				//
 +				if (qf)
 +				{
 +					if (lastqf) lastqf->next = qf;
 +					else qfiles = qf;
 +					lastqf = qf;
 +					while(lastqf->next) lastqf = lastqf->next;
 +				} //end if
 +			} //end else
 +			//
 +#if defined(WIN32)|defined(_WIN32)
 +			//find the next file
 +			done = !FindNextFile(handle, &filedata);
 +		} //end while
 +#else
 +		} //end for
 +		globfree(&globbuf);
 +#endif
 +	} //end if
 +	else
 +	{
 +#if defined(WIN32)|defined(_WIN32)
 +		handle = FindFirstFile(filter, &filedata);
 +		done = (handle == INVALID_HANDLE_VALUE);
 +		while(!done)
 +		{
 +			_splitpath(filter, filename, NULL, NULL, NULL);
 +			_splitpath(filter, NULL, &filename[strlen(filename)], NULL, NULL);
 +			AppendPathSeperator(filename, _MAX_PATH);
 +			strcat(filename, filedata.cFileName);
 +#else
 +		glob(filter, 0, NULL, &globbuf);
 +		for (j = 0; j < globbuf.gl_pathc; j++)
 +		{
 +			strcpy(filename, globbuf.gl_pathv[j]);
 +#endif
 +			//
 +			qf = malloc(sizeof(quakefile_t));
 +			if (!qf) Error("out of memory");
 +			memset(qf, 0, sizeof(quakefile_t));
 +			strcpy(qf->pakfile, "");
 +			strcpy(qf->filename, filename);
 +			strcpy(qf->origname, filename);
 +			qf->offset = 0;
 +			qf->length = 0;
 +			qf->type = QuakeFileType(filename);
 +			//add the file ot the list
 +			qf->next = NULL;
 +			if (lastqf) lastqf->next = qf;
 +			else qfiles = qf;
 +			lastqf = qf;
 +#if defined(WIN32)|defined(_WIN32)
 +			//find the next file
 +			done = !FindNextFile(handle, &filedata);
 +		} //end while
 +#else
 +		} //end for
 +		globfree(&globbuf);
 +#endif
 +	} //end else
 +	return qfiles;
 +} //end of the function FindQuakeFilesWithPakFilter
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +quakefile_t *FindQuakeFiles(char *filter)
 +{
 +	char *str;
 +	char newfilter[_MAX_PATH];
 +	char pakfilter[_MAX_PATH];
 +	char filefilter[_MAX_PATH];
 +
 +	strcpy(newfilter, filter);
 +	ConvertPath(newfilter);
 +	strcpy(pakfilter, newfilter);
 +
 +	str = StringContains(pakfilter, ".pak", false);
 +	if (!str) str = StringContains(pakfilter, ".pk3", false);
 +
 +	if (str)
 +	{
 +		str += strlen(".pak");
 +		if (*str)
 +		{
 +			*str++ = '\0';
 +			while(*str == '\\' || *str == '/') str++;
 +			strcpy(filefilter, str);
 +			return FindQuakeFilesWithPakFilter(pakfilter, filefilter);
 +		} //end if
 +	} //end else
 +	return FindQuakeFilesWithPakFilter(NULL, newfilter);
 +} //end of the function FindQuakeFiles
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int LoadQuakeFile(quakefile_t *qf, void **bufferptr)
 +{
 +	FILE *fp;
 +	void *buffer;
 +	int length;
 +	unzFile zf;
 +
 +	if (qf->zipfile)
 +	{
 +		//open the zip file
 +		zf = unzOpen(qf->pakfile);
 +		//set the file pointer
 +		qf->zipinfo.file = ((unz_s *) zf)->file;
 +		//open the Quake file in the zip file
 +		unzOpenCurrentFile(&qf->zipinfo);
 +		//allocate memory for the buffer
 +		length = qf->length;
 +		buffer = GetMemory(length+1);
 +		//read the Quake file from the zip file
 +		length = unzReadCurrentFile(&qf->zipinfo, buffer, length);
 +		//close the Quake file in the zip file
 +		unzCloseCurrentFile(&qf->zipinfo);
 +		//close the zip file
 +		unzClose(zf);
 +
 +		*bufferptr = buffer;
 +		return length;
 +	} //end if
 +	else
 +	{
 +		fp = SafeOpenRead(qf->filename);
 +		if (qf->offset) fseek(fp, qf->offset, SEEK_SET);
 +		length = qf->length;
 +		if (!length) length = Q_filelength(fp);
 +		buffer = GetMemory(length+1);
 +		((char *)buffer)[length] = 0;
 +		SafeRead(fp, buffer, length);
 +		fclose(fp);
 +
 +		*bufferptr = buffer;
 +		return length;
 +	} //end else
 +} //end of the function LoadQuakeFile
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int ReadQuakeFile(quakefile_t *qf, void *buffer, int offset, int length)
 +{
 +	FILE *fp;
 +	int read;
 +	unzFile zf;
 +	char tmpbuf[1024];
 +
 +	if (qf->zipfile)
 +	{
 +		//open the zip file
 +		zf = unzOpen(qf->pakfile);
 +		//set the file pointer
 +		qf->zipinfo.file = ((unz_s *) zf)->file;
 +		//open the Quake file in the zip file
 +		unzOpenCurrentFile(&qf->zipinfo);
 +		//
 +		while(offset > 0)
 +		{
 +			read = offset;
 +			if (read > sizeof(tmpbuf)) read = sizeof(tmpbuf);
 +			unzReadCurrentFile(&qf->zipinfo, tmpbuf, read);
 +			offset -= read;
 +		} //end while
 +		//read the Quake file from the zip file
 +		length = unzReadCurrentFile(&qf->zipinfo, buffer, length);
 +		//close the Quake file in the zip file
 +		unzCloseCurrentFile(&qf->zipinfo);
 +		//close the zip file
 +		unzClose(zf);
 +
 +		return length;
 +	} //end if
 +	else
 +	{
 +		fp = SafeOpenRead(qf->filename);
 +		if (qf->offset) fseek(fp, qf->offset, SEEK_SET);
 +		if (offset) fseek(fp, offset, SEEK_CUR);
 +		SafeRead(fp, buffer, length);
 +		fclose(fp);
 +
 +		return length;
 +	} //end else
 +} //end of the function ReadQuakeFile
 diff --git a/code/bspc/l_qfiles.h b/code/bspc/l_qfiles.h new file mode 100755 index 0000000..308c608 --- /dev/null +++ b/code/bspc/l_qfiles.h @@ -0,0 +1,91 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "../qcommon/unzip.h"
 +
 +#define QFILETYPE_UNKNOWN			0x8000
 +#define QFILETYPE_PAK				0x0001
 +#define QFILETYPE_PK3				0x0002
 +#define QFILETYPE_BSP				0x0004
 +#define QFILETYPE_MAP				0x0008
 +#define QFILETYPE_MDL				0x0010
 +#define QFILETYPE_MD2				0x0020
 +#define QFILETYPE_MD3				0x0040
 +#define QFILETYPE_WAL				0x0080
 +#define QFILETYPE_WAV				0x0100
 +#define QFILETYPE_AAS				0x4000
 +
 +#define QFILEEXT_UNKNOWN			""
 +#define QFILEEXT_PAK				".PAK"
 +#define QFILEEXT_PK3				".PK3"
 +#define QFILEEXT_SIN				".SIN"
 +#define QFILEEXT_BSP				".BSP"
 +#define QFILEEXT_MAP				".MAP"
 +#define QFILEEXT_MDL				".MDL"
 +#define QFILEEXT_MD2				".MD2"
 +#define QFILEEXT_MD3				".MD3"
 +#define QFILEEXT_WAL				".WAL"
 +#define QFILEEXT_WAV				".WAV"
 +#define QFILEEXT_AAS				".AAS"
 +
 +//maximum path length
 +#ifndef _MAX_PATH
 +	#define _MAX_PATH				1024
 +#endif
 +
 +//for Sin packs
 +#define MAX_PAK_FILENAME_LENGTH 120
 +#define SINPAKHEADER		(('K'<<24)+('A'<<16)+('P'<<8)+'S')
 +
 +typedef struct
 +{
 +	char	name[MAX_PAK_FILENAME_LENGTH];
 +	int	filepos, filelen;
 +} dsinpackfile_t;
 +
 +typedef struct quakefile_s
 +{
 +	char pakfile[_MAX_PATH];
 +	char filename[_MAX_PATH];
 +	char origname[_MAX_PATH];
 +	int zipfile;
 +	int type;
 +	int offset;
 +	int length;
 +	unz_s zipinfo;
 +	struct quakefile_s *next;
 +} quakefile_t;
 +
 +//returns the file extension for the given type
 +char *QuakeFileTypeExtension(int type);
 +//returns the file type for the given extension
 +int QuakeFileExtensionType(char *extension);
 +//return the Quake file type for the given file
 +int QuakeFileType(char *filename);
 +//returns true if the filename complies to the filter
 +int FileFilter(char *filter, char *filename, int casesensitive);
 +//find Quake files using the given filter
 +quakefile_t *FindQuakeFiles(char *filter);
 +//load the given Quake file, returns the length of the file
 +int LoadQuakeFile(quakefile_t *qf, void **bufferptr);
 +//read part of a Quake file into the buffer
 +int ReadQuakeFile(quakefile_t *qf, void *buffer, int offset, int length);
 diff --git a/code/bspc/l_threads.c b/code/bspc/l_threads.c new file mode 100755 index 0000000..bc4b998 --- /dev/null +++ b/code/bspc/l_threads.c @@ -0,0 +1,1510 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "l_cmd.h"
 +#include "l_threads.h"
 +#include "l_log.h"
 +#include "l_mem.h"
 +
 +#define	MAX_THREADS	64
 +
 +//#define THREAD_DEBUG
 +
 +int dispatch;
 +int workcount;
 +int oldf;
 +qboolean pacifier;
 +qboolean	threaded;
 +void (*workfunction) (int);
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int GetThreadWork(void)
 +{
 +	int	r;
 +	int	f;
 +
 +	ThreadLock();
 +
 +	if (dispatch == workcount)
 +	{
 +		ThreadUnlock ();
 +		return -1;
 +	}
 +
 +	f = 10*dispatch / workcount;
 +	if (f != oldf)
 +	{
 +		oldf = f;
 +		if (pacifier)
 +			printf ("%i...", f);
 +	} //end if
 +
 +	r = dispatch;
 +	dispatch++;
 +	ThreadUnlock ();
 +
 +	return r;
 +} //end of the function GetThreadWork
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadWorkerFunction(int threadnum)
 +{
 +	int		work;
 +
 +	while(1)
 +	{
 +		work = GetThreadWork ();
 +		if (work == -1)
 +			break;
 +//printf ("thread %i, work %i\n", threadnum, work);
 +		workfunction(work);
 +	} //end while
 +} //end of the function ThreadWorkerFunction
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int))
 +{
 +	if (numthreads == -1)
 +		ThreadSetDefault ();
 +	workfunction = func;
 +	RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
 +} //end of the function RunThreadsOnIndividual
 +
 +
 +//===================================================================
 +//
 +// WIN32
 +//
 +//===================================================================
 +
 +#if defined(WIN32) || defined(_WIN32)
 +
 +#define USED
 +
 +#include <windows.h>
 +
 +typedef struct thread_s
 +{
 +	HANDLE handle;
 +	int threadid;
 +	int id;
 +	struct thread_s *next;
 +} thread_t;
 +
 +thread_t *firstthread;
 +thread_t *lastthread;
 +int currentnumthreads;
 +int currentthreadid;
 +
 +int numthreads = 1;
 +CRITICAL_SECTION crit;
 +HANDLE semaphore;
 +static int enter;
 +static int numwaitingthreads = 0;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetDefault(void)
 +{
 +	SYSTEM_INFO info;
 +
 +	if (numthreads == -1)	// not set manually
 +	{
 +		GetSystemInfo (&info);
 +		numthreads = info.dwNumberOfProcessors;
 +		if (numthreads < 1 || numthreads > 32)
 +			numthreads = 1;
 +	} //end if
 +	qprintf ("%i threads\n", numthreads);
 +} //end of the function ThreadSetDefault
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadLock(void)
 +{
 +	if (!threaded)
 +	{
 +		Error("ThreadLock: !threaded");
 +		return;
 +	} //end if
 +	EnterCriticalSection(&crit);
 +	if (enter)
 +		Error("Recursive ThreadLock\n");
 +	enter = 1;
 +} //end of the function ThreadLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadUnlock (void)
 +{
 +	if (!threaded)
 +	{
 +		Error("ThreadUnlock: !threaded");
 +		return;
 +	} //end if
 +	if (!enter)
 +		Error("ThreadUnlock without lock\n");
 +	enter = 0;
 +	LeaveCriticalSection(&crit);
 +} //end of the function ThreadUnlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupLock(void)
 +{
 +	Log_Print("Win32 multi-threading\n");
 +	InitializeCriticalSection(&crit);
 +	threaded = true;	//Stupid me... forgot this!!!
 +	currentnumthreads = 0;
 +	currentthreadid = 0;
 +} //end of the function ThreadInitLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownLock(void)
 +{
 +	DeleteCriticalSection(&crit);
 +	threaded = false;	//Stupid me... forgot this!!!
 +} //end of the function ThreadShutdownLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupSemaphore(void)
 +{
 +	semaphore = CreateSemaphore(NULL, 0, 99999999, "bspc");
 +} //end of the function ThreadSetupSemaphore
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownSemaphore(void)
 +{
 +} //end of the function ThreadShutdownSemaphore
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSemaphoreWait(void)
 +{
 +	WaitForSingleObject(semaphore, INFINITE);
 +} //end of the function ThreadSemaphoreWait
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSemaphoreIncrease(int count)
 +{
 +	ReleaseSemaphore(semaphore, count, NULL);
 +} //end of the function ThreadSemaphoreIncrease
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int))
 +{
 +	int		threadid[MAX_THREADS];
 +	HANDLE	threadhandle[MAX_THREADS];
 +	int		i;
 +	int		start, end;
 +
 +	Log_Print("Win32 multi-threading\n");
 +	start = I_FloatTime ();
 +	dispatch = 0;
 +	workcount = workcnt;
 +	oldf = -1;
 +	pacifier = showpacifier;
 +	threaded = true;
 +
 +	if (numthreads == -1)
 +		ThreadSetDefault ();
 +
 +	if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1;
 +	//
 +	// run threads in parallel
 +	//
 +	InitializeCriticalSection (&crit);
 +
 +	numwaitingthreads = 0;
 +
 +	if (numthreads == 1)
 +	{	// use same thread
 +		func (0);
 +	} //end if
 +	else
 +	{
 +//		printf("starting %d threads\n", numthreads);
 +		for (i = 0; i < numthreads; i++)
 +		{
 +			threadhandle[i] = CreateThread(
 +			   NULL,	// LPSECURITY_ATTRIBUTES lpsa,
 +			   0,		// DWORD cbStack,
 +			   (LPTHREAD_START_ROUTINE)func,	// LPTHREAD_START_ROUTINE lpStartAddr,
 +			   (LPVOID)i,	// LPVOID lpvThreadParm,
 +			   0,			//   DWORD fdwCreate,
 +			   &threadid[i]);
 +//			printf("started thread %d\n", i);
 +		} //end for
 +
 +		for (i = 0; i < numthreads; i++)
 +			WaitForSingleObject (threadhandle[i], INFINITE);
 +	} //end else
 +	DeleteCriticalSection (&crit);
 +
 +	threaded = false;
 +	end = I_FloatTime ();
 +	if (pacifier) printf (" (%i)\n", end-start);
 +} //end of the function RunThreadsOn
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddThread(void (*func)(int))
 +{
 +	thread_t *thread;
 +
 +	if (numthreads == 1)
 +	{
 +		if (currentnumthreads >= numthreads) return;
 +		currentnumthreads++;
 +		func(-1);
 +		currentnumthreads--;
 +	} //end if
 +	else
 +	{
 +		ThreadLock();
 +		if (currentnumthreads >= numthreads)
 +		{
 +			ThreadUnlock();
 +			return;
 +		} //end if
 +		//allocate new thread
 +		thread = GetMemory(sizeof(thread_t));
 +		if (!thread) Error("can't allocate memory for thread\n");
 +
 +		//
 +		thread->threadid = currentthreadid;
 +		thread->handle = CreateThread(
 +				   NULL,	// LPSECURITY_ATTRIBUTES lpsa,
 +				   0,		// DWORD cbStack,
 +				   (LPTHREAD_START_ROUTINE)func,	// LPTHREAD_START_ROUTINE lpStartAddr,
 +				   (LPVOID) thread->threadid,			// LPVOID lpvThreadParm,
 +					0,						// DWORD fdwCreate,
 +					&thread->id);
 +
 +		//add the thread to the end of the list
 +		thread->next = NULL;
 +		if (lastthread) lastthread->next = thread;
 +		else firstthread = thread;
 +		lastthread = thread;
 +		//
 +#ifdef THREAD_DEBUG
 +		qprintf("added thread with id %d\n", thread->threadid);
 +#endif //THREAD_DEBUG
 +		//
 +		currentnumthreads++;
 +		currentthreadid++;
 +		//
 +		ThreadUnlock();
 +	} //end else
 +} //end of the function AddThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemoveThread(int threadid)
 +{
 +	thread_t *thread, *last;
 +
 +	//if a single thread
 +	if (threadid == -1) return;
 +	//
 +	ThreadLock();
 +	last = NULL;
 +	for (thread = firstthread; thread; thread = thread->next)
 +	{
 +		if (thread->threadid == threadid)
 +		{
 +			if (last) last->next = thread->next;
 +			else firstthread = thread->next;
 +			if (!thread->next) lastthread = last;
 +			//
 +			FreeMemory(thread);
 +			currentnumthreads--;
 +#ifdef THREAD_DEBUG
 +			qprintf("removed thread with id %d\n", threadid);
 +#endif //THREAD_DEBUG
 +			break;
 +		} //end if
 +		last = thread;
 +	} //end if
 +	if (!thread) Error("couldn't find thread with id %d", threadid);
 +	ThreadUnlock();
 +} //end of the function RemoveThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void WaitForAllThreadsFinished(void)
 +{
 +	HANDLE handle;
 +
 +	ThreadLock();
 +	while(firstthread)
 +	{
 +		handle = firstthread->handle;
 +		ThreadUnlock();
 +
 +		WaitForSingleObject(handle, INFINITE);
 +
 +		ThreadLock();
 +	} //end while
 +	ThreadUnlock();
 +} //end of the function WaitForAllThreadsFinished
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int GetNumThreads(void)
 +{
 +	return currentnumthreads;
 +} //end of the function GetNumThreads
 +
 +#endif
 +
 +
 +//===================================================================
 +//
 +// OSF1
 +//
 +//===================================================================
 +
 +#if defined(__osf__)
 +
 +#define	USED
 +
 +#include <pthread.h>
 +
 +typedef struct thread_s
 +{
 +	pthread_t thread;
 +	int threadid;
 +	int id;
 +	struct thread_s *next;
 +} thread_t;
 +
 +thread_t *firstthread;
 +thread_t *lastthread;
 +int currentnumthreads;
 +int currentthreadid;
 +
 +int numthreads = 1;
 +pthread_mutex_t my_mutex;
 +pthread_attr_t	attrib;
 +static int enter;
 +static int numwaitingthreads = 0;
 +
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetDefault(void)
 +{
 +	if (numthreads == -1)	// not set manually
 +	{
 +		numthreads = 1;
 +	} //end if
 +	qprintf("%i threads\n", numthreads);
 +} //end of the function ThreadSetDefault
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadLock(void)
 +{
 +	if (!threaded)
 +	{
 +		Error("ThreadLock: !threaded");
 +		return;
 +	} //end if
 +	if (my_mutex)
 +	{
 +		pthread_mutex_lock(my_mutex);
 +	} //end if
 +	if (enter)
 +		Error("Recursive ThreadLock\n");
 +	enter = 1;
 +} //end of the function ThreadLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadUnlock(void)
 +{
 +	if (!threaded)
 +	{
 +		Error("ThreadUnlock: !threaded");
 +		return;
 +	} //end if
 +	if (!enter)
 +		Error("ThreadUnlock without lock\n");
 +	enter = 0;
 +	if (my_mutex)
 +	{
 +		pthread_mutex_unlock(my_mutex);
 +	} //end if
 +} //end of the function ThreadUnlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupLock(void)
 +{
 +	pthread_mutexattr_t mattrib;
 +
 +	Log_Print("pthread multi-threading\n");
 +
 +	if (!my_mutex)
 +	{
 +		my_mutex = GetMemory(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");
 +
 +	threaded = true;
 +	currentnumthreads = 0;
 +	currentthreadid = 0;
 +} //end of the function ThreadInitLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownLock(void)
 +{
 +	threaded = false;
 +} //end of the function ThreadShutdownLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int))
 +{
 +	int		i;
 +	pthread_t	work_threads[MAX_THREADS];
 +	pthread_addr_t	status;
 +	pthread_attr_t	attrib;
 +	pthread_mutexattr_t	mattrib;
 +	int		start, end;
 +
 +	Log_Print("pthread multi-threading\n");
 +
 +	start = I_FloatTime ();
 +	dispatch = 0;
 +	workcount = workcnt;
 +	oldf = -1;
 +	pacifier = showpacifier;
 +	threaded = true;
 +
 +	if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1;
 +
 +	if (pacifier)
 +		setbuf (stdout, NULL);
 +
 +	if (!my_mutex)
 +	{
 +		my_mutex = GetMemory(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
 +		, (pthread_startroutine_t)func, (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");
 +	}
 +
 +	threaded = false;
 +
 +	end = I_FloatTime ();
 +	if (pacifier)
 +		printf (" (%i)\n", end-start);
 +} //end of the function RunThreadsOn
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddThread(void (*func)(int))
 +{
 +	thread_t *thread;
 +
 +	if (numthreads == 1)
 +	{
 +		if (currentnumthreads >= numthreads) return;
 +		currentnumthreads++;
 +		func(-1);
 +		currentnumthreads--;
 +	} //end if
 +	else
 +	{
 +		ThreadLock();
 +		if (currentnumthreads >= numthreads)
 +		{
 +			ThreadUnlock();
 +			return;
 +		} //end if
 +		//allocate new thread
 +		thread = GetMemory(sizeof(thread_t));
 +		if (!thread) Error("can't allocate memory for thread\n");
 +		//
 +		thread->threadid = currentthreadid;
 +
 +		if (pthread_create(&thread->thread, attrib, (pthread_startroutine_t)func, (pthread_addr_t)thread->threadid) == -1)
 +			Error ("pthread_create failed");
 +
 +		//add the thread to the end of the list
 +		thread->next = NULL;
 +		if (lastthread) lastthread->next = thread;
 +		else firstthread = thread;
 +		lastthread = thread;
 +		//
 +#ifdef THREAD_DEBUG
 +		qprintf("added thread with id %d\n", thread->threadid);
 +#endif //THREAD_DEBUG
 +		//
 +		currentnumthreads++;
 +		currentthreadid++;
 +		//
 +		ThreadUnlock();
 +	} //end else
 +} //end of the function AddThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemoveThread(int threadid)
 +{
 +	thread_t *thread, *last;
 +
 +	//if a single thread
 +	if (threadid == -1) return;
 +	//
 +	ThreadLock();
 +	last = NULL;
 +	for (thread = firstthread; thread; thread = thread->next)
 +	{
 +		if (thread->threadid == threadid)
 +		{
 +			if (last) last->next = thread->next;
 +			else firstthread = thread->next;
 +			if (!thread->next) lastthread = last;
 +			//
 +			FreeMemory(thread);
 +			currentnumthreads--;
 +#ifdef THREAD_DEBUG
 +			qprintf("removed thread with id %d\n", threadid);
 +#endif //THREAD_DEBUG
 +			break;
 +		} //end if
 +		last = thread;
 +	} //end if
 +	if (!thread) Error("couldn't find thread with id %d", threadid);
 +	ThreadUnlock();
 +} //end of the function RemoveThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void WaitForAllThreadsFinished(void)
 +{
 +	pthread_t *thread;
 +	pthread_addr_t	status;
 +
 +	ThreadLock();
 +	while(firstthread)
 +	{
 +		thread = &firstthread->thread;
 +		ThreadUnlock();
 +
 +		if (pthread_join(*thread, &status) == -1)
 +			Error("pthread_join failed");
 +
 +		ThreadLock();
 +	} //end while
 +	ThreadUnlock();
 +} //end of the function WaitForAllThreadsFinished
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int GetNumThreads(void)
 +{
 +	return currentnumthreads;
 +} //end of the function GetNumThreads
 +
 +#endif
 +
 +//===================================================================
 +//
 +// LINUX
 +//
 +//===================================================================
 +
 +#if defined(LINUX)
 +
 +#define	USED
 +
 +#include <pthread.h>
 +#include <semaphore.h>
 +
 +typedef struct thread_s
 +{
 +	pthread_t thread;
 +	int threadid;
 +	int id;
 +	struct thread_s *next;
 +} thread_t;
 +
 +thread_t *firstthread;
 +thread_t *lastthread;
 +int currentnumthreads;
 +int currentthreadid;
 +
 +int numthreads = 1;
 +pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
 +pthread_attr_t	attrib;
 +sem_t semaphore;
 +static int enter;
 +static int numwaitingthreads = 0;
 +
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetDefault(void)
 +{
 +	if (numthreads == -1)	// not set manually
 +	{
 +		numthreads = 1;
 +	} //end if
 +	qprintf("%i threads\n", numthreads);
 +} //end of the function ThreadSetDefault
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadLock(void)
 +{
 +	if (!threaded)
 +	{
 +		Error("ThreadLock: !threaded");
 +		return;
 +	} //end if
 +	pthread_mutex_lock(&my_mutex);
 +	if (enter)
 +		Error("Recursive ThreadLock\n");
 +	enter = 1;
 +} //end of the function ThreadLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadUnlock(void)
 +{
 +	if (!threaded)
 +	{
 +		Error("ThreadUnlock: !threaded");
 +		return;
 +	} //end if
 +	if (!enter)
 +		Error("ThreadUnlock without lock\n");
 +	enter = 0;
 +	pthread_mutex_unlock(&my_mutex);
 +} //end of the function ThreadUnlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupLock(void)
 +{
 +	pthread_mutexattr_t mattrib;
 +
 +	Log_Print("pthread multi-threading\n");
 +
 +	threaded = true;
 +	currentnumthreads = 0;
 +	currentthreadid = 0;
 +} //end of the function ThreadInitLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownLock(void)
 +{
 +	threaded = false;
 +} //end of the function ThreadShutdownLock
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupSemaphore(void)
 +{
 +	sem_init(&semaphore, 0, 0);
 +} //end of the function ThreadSetupSemaphore
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownSemaphore(void)
 +{
 +	sem_destroy(&semaphore);
 +} //end of the function ThreadShutdownSemaphore
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSemaphoreWait(void)
 +{
 +	sem_wait(&semaphore);
 +} //end of the function ThreadSemaphoreWait
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSemaphoreIncrease(int count)
 +{
 +	int i;
 +
 +	for (i = 0; i < count; i++)
 +	{
 +		sem_post(&semaphore);
 +	} //end for
 +} //end of the function ThreadSemaphoreIncrease
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int))
 +{
 +	int		i;
 +	pthread_t	work_threads[MAX_THREADS];
 +	void *pthread_return;
 +	pthread_attr_t	attrib;
 +	pthread_mutexattr_t	mattrib;
 +	int		start, end;
 +
 +	Log_Print("pthread multi-threading\n");
 +
 +	start = I_FloatTime ();
 +	dispatch = 0;
 +	workcount = workcnt;
 +	oldf = -1;
 +	pacifier = showpacifier;
 +	threaded = true;
 +
 +	if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1;
 +
 +	if (pacifier)
 +		setbuf (stdout, NULL);
 +
 +	for (i=0 ; i<numthreads ; i++)
 +	{
 +		if (pthread_create(&work_threads[i], NULL, (void *)func, (void *)i) == -1)
 +			Error ("pthread_create failed");
 +	}
 +		
 +	for (i=0 ; i<numthreads ; i++)
 +	{
 +		if (pthread_join(work_threads[i], &pthread_return) == -1)
 +			Error ("pthread_join failed");
 +	}
 +
 +	threaded = false;
 +
 +	end = I_FloatTime ();
 +	if (pacifier)
 +		printf (" (%i)\n", end-start);
 +} //end of the function RunThreadsOn
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddThread(void (*func)(int))
 +{
 +	thread_t *thread;
 +
 +	if (numthreads == 1)
 +	{
 +		if (currentnumthreads >= numthreads) return;
 +		currentnumthreads++;
 +		func(-1);
 +		currentnumthreads--;
 +	} //end if
 +	else
 +	{
 +		ThreadLock();
 +		if (currentnumthreads >= numthreads)
 +		{
 +			ThreadUnlock();
 +			return;
 +		} //end if
 +		//allocate new thread
 +		thread = GetMemory(sizeof(thread_t));
 +		if (!thread) Error("can't allocate memory for thread\n");
 +		//
 +		thread->threadid = currentthreadid;
 +
 +		if (pthread_create(&thread->thread, NULL, (void *)func, (void *)thread->threadid) == -1)
 +			Error ("pthread_create failed");
 +
 +		//add the thread to the end of the list
 +		thread->next = NULL;
 +		if (lastthread) lastthread->next = thread;
 +		else firstthread = thread;
 +		lastthread = thread;
 +		//
 +#ifdef THREAD_DEBUG
 +		qprintf("added thread with id %d\n", thread->threadid);
 +#endif //THREAD_DEBUG
 +		//
 +		currentnumthreads++;
 +		currentthreadid++;
 +		//
 +		ThreadUnlock();
 +	} //end else
 +} //end of the function AddThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemoveThread(int threadid)
 +{
 +	thread_t *thread, *last;
 +
 +	//if a single thread
 +	if (threadid == -1) return;
 +	//
 +	ThreadLock();
 +	last = NULL;
 +	for (thread = firstthread; thread; thread = thread->next)
 +	{
 +		if (thread->threadid == threadid)
 +		{
 +			if (last) last->next = thread->next;
 +			else firstthread = thread->next;
 +			if (!thread->next) lastthread = last;
 +			//
 +			FreeMemory(thread);
 +			currentnumthreads--;
 +#ifdef THREAD_DEBUG
 +			qprintf("removed thread with id %d\n", threadid);
 +#endif //THREAD_DEBUG
 +			break;
 +		} //end if
 +		last = thread;
 +	} //end if
 +	if (!thread) Error("couldn't find thread with id %d", threadid);
 +	ThreadUnlock();
 +} //end of the function RemoveThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void WaitForAllThreadsFinished(void)
 +{
 +	pthread_t *thread;
 +	void *pthread_return;
 +
 +	ThreadLock();
 +	while(firstthread)
 +	{
 +		thread = &firstthread->thread;
 +		ThreadUnlock();
 +
 +		if (pthread_join(*thread, &pthread_return) == -1)
 +			Error("pthread_join failed");
 +
 +		ThreadLock();
 +	} //end while
 +	ThreadUnlock();
 +} //end of the function WaitForAllThreadsFinished
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int GetNumThreads(void)
 +{
 +	return currentnumthreads;
 +} //end of the function GetNumThreads
 +
 +#endif //LINUX
 +
 +
 +//===================================================================
 +//
 +// IRIX
 +//
 +//===================================================================
 +
 +#ifdef _MIPS_ISA 
 +
 +#define	USED
 +
 +#include <task.h>
 +#include <abi_mutex.h>
 +#include <sys/types.h>
 +#include <sys/prctl.h>
 +
 +typedef struct thread_s
 +{
 +	int threadid;
 +	int id;
 +	struct thread_s *next;
 +} thread_t;
 +
 +thread_t *firstthread;
 +thread_t *lastthread;
 +int currentnumthreads;
 +int currentthreadid;
 +
 +int numthreads = 1;
 +static int enter;
 +static int numwaitingthreads = 0;
 +
 +abilock_t		lck;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetDefault (void)
 +{
 +	if (numthreads == -1)
 +		numthreads = prctl(PR_MAXPPROCS);
 +	printf ("%i threads\n", numthreads);
 +//@@
 +	usconfig (CONF_INITUSERS, numthreads);
 +} //end of the function ThreadSetDefault
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadLock (void)
 +{
 +	spin_lock (&lck);
 +} //end of the function ThreadLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadUnlock (void)
 +{
 +	release_lock(&lck);
 +} //end of the function ThreadUnlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupLock(void)
 +{
 +	init_lock (&lck);
 +
 +	Log_Print("IRIX multi-threading\n");
 +
 +	threaded = true;
 +	currentnumthreads = 0;
 +	currentthreadid = 0;
 +} //end of the function ThreadInitLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownLock(void)
 +{
 +	threaded = false;
 +} //end of the function ThreadShutdownLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
 +{
 +	int		i;
 +	int		pid[MAX_THREADS];
 +	int		start, end;
 +
 +	start = I_FloatTime ();
 +	dispatch = 0;
 +	workcount = workcnt;
 +	oldf = -1;
 +	pacifier = showpacifier;
 +	threaded = true;
 +
 +	if (numthreads < 1 || numthreads > MAX_THREADS) numthreads = 1;
 +
 +	if (pacifier)
 +		setbuf (stdout, NULL);
 +
 +	init_lock (&lck);
 +
 +	for (i=0 ; i<numthreads-1 ; i++)
 +	{
 +		pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
 +			, NULL, 0x100000);
 +//		pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
 +//			, NULL, 0x80000);
 +		if (pid[i] == -1)
 +		{
 +			perror ("sproc");
 +			Error ("sproc failed");
 +		}
 +	}
 +		
 +	func(i);
 +			
 +	for (i=0 ; i<numthreads-1 ; i++)
 +		wait (NULL);
 +
 +	threaded = false;
 +
 +	end = I_FloatTime ();
 +	if (pacifier)
 +		printf (" (%i)\n", end-start);
 +} //end of the function RunThreadsOn
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddThread(void (*func)(int))
 +{
 +	thread_t *thread;
 +
 +	if (numthreads == 1)
 +	{
 +		if (currentnumthreads >= numthreads) return;
 +		currentnumthreads++;
 +		func(-1);
 +		currentnumthreads--;
 +	} //end if
 +	else
 +	{
 +		ThreadLock();
 +		if (currentnumthreads >= numthreads)
 +		{
 +			ThreadUnlock();
 +			return;
 +		} //end if
 +		//allocate new thread
 +		thread = GetMemory(sizeof(thread_t));
 +		if (!thread) Error("can't allocate memory for thread\n");
 +		//
 +		thread->threadid = currentthreadid;
 +
 +		thread->id = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)thread->threadid, NULL, 0x100000);
 +		if (thread->id == -1)
 +		{
 +			perror ("sproc");
 +			Error ("sproc failed");
 +		}
 +
 +		//add the thread to the end of the list
 +		thread->next = NULL;
 +		if (lastthread) lastthread->next = thread;
 +		else firstthread = thread;
 +		lastthread = thread;
 +		//
 +#ifdef THREAD_DEBUG
 +		qprintf("added thread with id %d\n", thread->threadid);
 +#endif //THREAD_DEBUG
 +		//
 +		currentnumthreads++;
 +		currentthreadid++;
 +		//
 +		ThreadUnlock();
 +	} //end else
 +} //end of the function AddThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemoveThread(int threadid)
 +{
 +	thread_t *thread, *last;
 +
 +	//if a single thread
 +	if (threadid == -1) return;
 +	//
 +	ThreadLock();
 +	last = NULL;
 +	for (thread = firstthread; thread; thread = thread->next)
 +	{
 +		if (thread->threadid == threadid)
 +		{
 +			if (last) last->next = thread->next;
 +			else firstthread = thread->next;
 +			if (!thread->next) lastthread = last;
 +			//
 +			FreeMemory(thread);
 +			currentnumthreads--;
 +#ifdef THREAD_DEBUG
 +			qprintf("removed thread with id %d\n", threadid);
 +#endif //THREAD_DEBUG
 +			break;
 +		} //end if
 +		last = thread;
 +	} //end if
 +	if (!thread) Error("couldn't find thread with id %d", threadid);
 +	ThreadUnlock();
 +} //end of the function RemoveThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void WaitForAllThreadsFinished(void)
 +{
 +	ThreadLock();
 +	while(firstthread)
 +	{
 +		ThreadUnlock();
 +
 +		//wait (NULL);
 +
 +		ThreadLock();
 +	} //end while
 +	ThreadUnlock();
 +} //end of the function WaitForAllThreadsFinished
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int GetNumThreads(void)
 +{
 +	return currentnumthreads;
 +} //end of the function GetNumThreads
 +
 +#endif //_MIPS_ISA
 +
 +
 +//=======================================================================
 +//
 +// SINGLE THREAD
 +//
 +//=======================================================================
 +
 +#ifndef USED
 +
 +int numthreads = 1;
 +int currentnumthreads = 0;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetDefault(void)
 +{
 +	numthreads = 1;
 +} //end of the function ThreadSetDefault
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadLock(void)
 +{
 +} //end of the function ThreadLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadUnlock(void)
 +{
 +} //end of the function ThreadUnlock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupLock(void)
 +{
 +	Log_Print("no multi-threading\n");
 +} //end of the function ThreadInitLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownLock(void)
 +{
 +} //end of the function ThreadShutdownLock
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSetupSemaphore(void)
 +{
 +} //end of the function ThreadSetupSemaphore
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadShutdownSemaphore(void)
 +{
 +} //end of the function ThreadShutdownSemaphore
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSemaphoreWait(void)
 +{
 +} //end of the function ThreadSemaphoreWait
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ThreadSemaphoreIncrease(int count)
 +{
 +} //end of the function ThreadSemaphoreIncrease
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RunThreadsOn(int workcnt, qboolean showpacifier, void(*func)(int))
 +{
 +	int start, end;
 +
 +	Log_Print("no multi-threading\n");
 +	dispatch = 0;
 +	workcount = workcnt;
 +	oldf = -1;
 +	pacifier = showpacifier;
 +	start = I_FloatTime (); 
 +#ifdef NeXT
 +	if (pacifier)
 +		setbuf (stdout, NULL);
 +#endif
 +	func(0);
 +
 +	end = I_FloatTime ();
 +	if (pacifier)
 +		printf (" (%i)\n", end-start);
 +} //end of the function RunThreadsOn
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddThread(void (*func)(int))
 +{
 +	if (currentnumthreads >= numthreads) return;
 +	currentnumthreads++;
 +	func(-1);
 +	currentnumthreads--;
 +} //end of the function AddThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemoveThread(int threadid)
 +{
 +} //end of the function RemoveThread
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void WaitForAllThreadsFinished(void)
 +{
 +} //end of the function WaitForAllThreadsFinished
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int GetNumThreads(void)
 +{
 +	return currentnumthreads;
 +} //end of the function GetNumThreads
 +
 +#endif //USED
 diff --git a/code/bspc/l_threads.h b/code/bspc/l_threads.h new file mode 100755 index 0000000..2c6d8df --- /dev/null +++ b/code/bspc/l_threads.h @@ -0,0 +1,45 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +extern int numthreads;
 +
 +void ThreadSetDefault (void);
 +int GetThreadWork (void);
 +void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int));
 +void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int));
 +
 +//mutex
 +void ThreadSetupLock(void);
 +void ThreadShutdownLock(void);
 +void ThreadLock (void);
 +void ThreadUnlock (void);
 +//semaphore
 +void ThreadSetupSemaphore(void);
 +void ThreadShutdownSemaphore(void);
 +void ThreadSemaphoreWait(void);
 +void ThreadSemaphoreIncrease(int count);
 +//add/remove threads
 +void AddThread(void (*func)(int));
 +void RemoveThread(int threadid);
 +void WaitForAllThreadsFinished(void);
 +int GetNumThreads(void);
 +
 diff --git a/code/bspc/l_utils.c b/code/bspc/l_utils.c new file mode 100755 index 0000000..f9d6d5f --- /dev/null +++ b/code/bspc/l_utils.c @@ -0,0 +1,259 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//#ifndef BOTLIB
 +//#define BOTLIB
 +//#endif //BOTLIB
 +
 +#ifdef BOTLIB
 +#include "q_shared.h"
 +#include "qfiles.h"
 +#include "botlib.h"
 +#include "l_log.h"
 +#include "l_libvar.h"
 +#include "l_memory.h"
 +//#include "l_utils.h"
 +#include "be_interface.h"
 +#else //BOTLIB
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#endif //BOTLIB
 +
 +#ifdef BOTLIB
 +//========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//========================================================================
 +void Vector2Angles(vec3_t value1, vec3_t angles)
 +{
 +	float	forward;
 +	float	yaw, pitch;
 +	
 +	if (value1[1] == 0 && value1[0] == 0)
 +	{
 +		yaw = 0;
 +		if (value1[2] > 0) pitch = 90;
 +		else pitch = 270;
 +	} //end if
 +	else
 +	{
 +		yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
 +		if (yaw < 0) yaw += 360;
 +
 +		forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
 +		pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
 +		if (pitch < 0) pitch += 360;
 +	} //end else
 +
 +	angles[PITCH] = -pitch;
 +	angles[YAW] = yaw;
 +	angles[ROLL] = 0;
 +} //end of the function Vector2Angles
 +#endif //BOTLIB
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ConvertPath(char *path)
 +{
 +	while(*path)
 +	{
 +		if (*path == '/' || *path == '\\') *path = PATHSEPERATOR_CHAR;
 +		path++;
 +	} //end while
 +} //end of the function ConvertPath
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AppendPathSeperator(char *path, int length)
 +{
 +	int pathlen = strlen(path);
 +
 +	if (strlen(path) && length-pathlen > 1 && path[pathlen-1] != '/' && path[pathlen-1] != '\\')
 +	{
 +		path[pathlen] = PATHSEPERATOR_CHAR;
 +		path[pathlen+1] = '\0';
 +	} //end if
 +} //end of the function AppenPathSeperator
 +
 +#if 0
 +//===========================================================================
 +// returns pointer to file handle
 +// sets offset to and length of 'filename' in the pak file
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean FindFileInPak(char *pakfile, char *filename, foundfile_t *file)
 +{
 +	FILE *fp;
 +	dpackheader_t packheader;
 +	dpackfile_t *packfiles;
 +	int numdirs, i;
 +	char path[MAX_PATH];
 +
 +	//open the pak file
 +	fp = fopen(pakfile, "rb");
 +	if (!fp)
 +	{
 +		return false;
 +	} //end if
 +	//read pak header, check for valid pak id and seek to the dir entries
 +	if ((fread(&packheader, 1, sizeof(dpackheader_t), fp) != sizeof(dpackheader_t))
 +		|| (packheader.ident != IDPAKHEADER)
 +		||	(fseek(fp, LittleLong(packheader.dirofs), SEEK_SET))
 +		)
 +	{
 +		fclose(fp);
 +		return false;
 +	} //end if
 +	//number of dir entries in the pak file
 +	numdirs = LittleLong(packheader.dirlen) / sizeof(dpackfile_t);
 +	packfiles = (dpackfile_t *) GetMemory(numdirs * sizeof(dpackfile_t));
 +	//read the dir entry
 +	if (fread(packfiles, sizeof(dpackfile_t), numdirs, fp) != numdirs)
 +	{
 +		fclose(fp);
 +		FreeMemory(packfiles);
 +		return false;
 +	} //end if
 +	fclose(fp);
 +	//
 +	strcpy(path, filename);
 +	ConvertPath(path);
 +	//find the dir entry in the pak file
 +	for (i = 0; i < numdirs; i++)
 +	{
 +		//convert the dir entry name
 +		ConvertPath(packfiles[i].name);
 +		//compare the dir entry name with the filename
 +		if (Q_strcasecmp(packfiles[i].name, path) == 0)
 +		{
 +			strcpy(file->filename, pakfile);
 +			file->offset = LittleLong(packfiles[i].filepos);
 +			file->length = LittleLong(packfiles[i].filelen);
 +			FreeMemory(packfiles);
 +			return true;
 +		} //end if
 +	} //end for
 +	FreeMemory(packfiles);
 +	return false;
 +} //end of the function FindFileInPak
 +//===========================================================================
 +// find a Quake2 file
 +// returns full path in 'filename'
 +// sets offset and length of the file
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean FindQuakeFile2(char *basedir, char *gamedir, char *filename, foundfile_t *file)
 +{
 +	int dir, i;
 +	//NOTE: 3 is necessary (LCC bug???)
 +	char gamedirs[3][MAX_PATH] = {"","",""};
 +	char filedir[MAX_PATH] = "";
 +
 +	//
 +	if (gamedir) strncpy(gamedirs[0], gamedir, MAX_PATH);
 +	strncpy(gamedirs[1], "baseq2", MAX_PATH);
 +	//
 +	//find the file in the two game directories
 +	for (dir = 0; dir < 2; dir++)
 +	{
 +		//check if the file is in a directory
 +		filedir[0] = 0;
 +		if (basedir && strlen(basedir))
 +		{
 +			strncpy(filedir, basedir, MAX_PATH);
 +			AppendPathSeperator(filedir, MAX_PATH);
 +		} //end if
 +		if (strlen(gamedirs[dir]))
 +		{
 +			strncat(filedir, gamedirs[dir], MAX_PATH - strlen(filedir));
 +			AppendPathSeperator(filedir, MAX_PATH);
 +		} //end if
 +		strncat(filedir, filename, MAX_PATH - strlen(filedir));
 +		ConvertPath(filedir);
 +		Log_Write("accessing %s", filedir);
 +		if (!access(filedir, 0x04))
 +		{
 +			strcpy(file->filename, filedir);
 +			file->length = 0;
 +			file->offset = 0;
 +			return true;
 +		} //end if
 +		//check if the file is in a pak?.pak
 +		for (i = 0; i < 10; i++)
 +		{
 +			filedir[0] = 0;
 +			if (basedir && strlen(basedir))
 +			{
 +				strncpy(filedir, basedir, MAX_PATH);
 +				AppendPathSeperator(filedir, MAX_PATH);
 +			} //end if
 +			if (strlen(gamedirs[dir]))
 +			{
 +				strncat(filedir, gamedirs[dir], MAX_PATH - strlen(filedir));
 +				AppendPathSeperator(filedir, MAX_PATH);
 +			} //end if
 +			sprintf(&filedir[strlen(filedir)], "pak%d.pak\0", i);
 +			if (!access(filedir, 0x04))
 +			{
 +				Log_Write("searching %s in %s", filename, filedir);
 +				if (FindFileInPak(filedir, filename, file)) return true;
 +			} //end if
 +		} //end for
 +	} //end for
 +	file->offset = 0;
 +	file->length = 0;
 +	return false;
 +} //end of the function FindQuakeFile2
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#ifdef BOTLIB
 +qboolean FindQuakeFile(char *filename, foundfile_t *file)
 +{
 +	return FindQuakeFile2(LibVarGetString("basedir"),
 +				LibVarGetString("gamedir"), filename, file);
 +} //end of the function FindQuakeFile
 +#else //BOTLIB
 +qboolean FindQuakeFile(char *basedir, char *gamedir, char *filename, foundfile_t *file)
 +{
 +	return FindQuakeFile2(basedir, gamedir, filename, file);
 +} //end of the function FindQuakeFile
 +#endif //BOTLIB
 +
 +#endif
 diff --git a/code/bspc/l_utils.h b/code/bspc/l_utils.h new file mode 100755 index 0000000..2ef9161 --- /dev/null +++ b/code/bspc/l_utils.h @@ -0,0 +1,79 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#ifndef MAX_PATH
 +	#define MAX_PATH			64
 +#endif
 +
 +#ifndef PATH_SEPERATORSTR
 +	#if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__)
 +		#define PATHSEPERATOR_STR		"\\"
 +	#else
 +		#define PATHSEPERATOR_STR		"/"
 +	#endif
 +#endif
 +#ifndef PATH_SEPERATORCHAR
 +	#if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__)
 +		#define PATHSEPERATOR_CHAR		'\\'
 +	#else
 +		#define PATHSEPERATOR_CHAR		'/'
 +	#endif
 +#endif
 +
 +//random in the range [0, 1]
 +#define random()			((rand () & 0x7fff) / ((float)0x7fff))
 +//random in the range [-1, 1]
 +#define crandom()			(2.0 * (random() - 0.5))
 +//min and max
 +#define Maximum(x,y)		(x > y ? x : y)
 +#define Minimum(x,y)		(x < y ? x : y)
 +//absolute value
 +#define FloatAbs(x)		(*(float *) &((* (int *) &(x)) & 0x7FFFFFFF))
 +#define IntAbs(x)			(~(x))
 +//coordinates
 +#define _X		0
 +#define _Y		1
 +#define _Z		2
 +
 +typedef struct foundfile_s
 +{
 +	int offset;
 +	int length;
 +	char filename[MAX_PATH];		//screw LCC, array must be at end of struct
 +} foundfile_t;
 +
 +void Vector2Angles(vec3_t value1, vec3_t angles);
 +//set the correct path seperators
 +void ConvertPath(char *path);
 +//append a path seperator to the given path not exceeding the length
 +void AppendPathSeperator(char *path, int length);
 +//find a file in a pak file
 +qboolean FindFileInPak(char *pakfile, char *filename, foundfile_t *file);
 +//find a quake file
 +#ifdef BOTLIB
 +qboolean FindQuakeFile(char *filename, foundfile_t *file);
 +#else //BOTLIB
 +qboolean FindQuakeFile(char *basedir, char *gamedir, char *filename, foundfile_t *file);
 +#endif //BOTLIB
 +
 +
 +
 diff --git a/code/bspc/lcc.mak b/code/bspc/lcc.mak new file mode 100755 index 0000000..d74adce --- /dev/null +++ b/code/bspc/lcc.mak @@ -0,0 +1,61 @@ +#
 +# Makefile for the BSPC tool for the Gladiator Bot
 +# Intended for LCC-Win32
 +#
 +
 +CC=lcc
 +CFLAGS=-DC_ONLY -o
 +OBJS= _files.obj\
 +	aas_areamerging.obj\
 +	aas_cfg.obj\
 +	aas_create.obj\
 +	aas_edgemelting.obj\
 +	aas_facemerging.obj\
 +	aas_file.obj\
 +	aas_gsubdiv.obj\
 +	aas_map.obj\
 +	aas_prunenodes.obj\
 +	aas_store.obj\
 +	brushbsp.obj\
 +	bspc.obj\
 +	csg.obj\
 +	faces.obj\
 +	glfile.obj\
 +	l_bsp_hl.obj\
 +	l_bsp_q1.obj\
 +	l_bsp_q2.obj\
 +	l_bsp_sin.obj\
 +	l_cmd.obj\
 +	l_log.obj\
 +	l_math.obj\
 +	l_mem.obj\
 +	l_poly.obj\
 +	l_qfiles.obj\
 +	l_script.obj\
 +	l_threads.obj\
 +	l_utils.obj\
 +	leakfile.obj\
 +	map.obj\
 +	map_hl.obj\
 +	map_q1.obj\
 +	map_q2.obj\
 +	map_q2_new.obj\
 +	map_sin.obj\
 +	nodraw.obj\
 +	portals.obj\
 +	prtfile.obj\
 +	textures.obj\
 +	tree.obj\
 +	writebsp.obj
 +
 +all:	bspc.exe
 +
 +bspc.exe:	$(OBJS)
 +	lcclnk
 +
 +clean:
 +	del *.obj bspc.exe
 +
 +%.obj: %.c
 +	$(CC) $(CFLAGS) $<
 +
 diff --git a/code/bspc/leakfile.c b/code/bspc/leakfile.c new file mode 100755 index 0000000..7118759 --- /dev/null +++ b/code/bspc/leakfile.c @@ -0,0 +1,101 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +
 +/*
 +==============================================================================
 +
 +LEAF FILE GENERATION
 +
 +Save out name.line for qe3 to read
 +==============================================================================
 +*/
 +
 +
 +/*
 +=============
 +LeakFile
 +
 +Finds the shortest possible chain of portals
 +that leads from the outside leaf to a specifically
 +occupied leaf
 +=============
 +*/
 +void LeakFile (tree_t *tree)
 +{
 +	vec3_t	mid;
 +	FILE	*linefile;
 +	char	filename[1024];
 +	node_t	*node;
 +	int		count;
 +
 +	if (!tree->outside_node.occupied)
 +		return;
 +
 +	qprintf ("--- LeakFile ---\n");
 +
 +	//
 +	// write the points to the file
 +	//
 +	sprintf (filename, "%s.lin", source);
 +	qprintf ("%s\n", filename);
 +	linefile = fopen (filename, "w");
 +	if (!linefile)
 +		Error ("Couldn't open %s\n", filename);
 +
 +	count = 0;
 +	node = &tree->outside_node;
 +	while (node->occupied > 1)
 +	{
 +		int			next;
 +		portal_t	*p, *nextportal;
 +		node_t		*nextnode;
 +		int			s;
 +
 +		// find the best portal exit
 +		next = node->occupied;
 +		for (p=node->portals ; p ; p = p->next[!s])
 +		{
 +			s = (p->nodes[0] == node);
 +			if (p->nodes[s]->occupied
 +				&& p->nodes[s]->occupied < next)
 +			{
 +				nextportal = p;
 +				nextnode = p->nodes[s];
 +				next = nextnode->occupied;
 +			}
 +		}
 +		node = nextnode;
 +		WindingCenter (nextportal->winding, mid);
 +		fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
 +		count++;
 +	}
 +	// add the occupant center
 +	GetVectorForKey (node->occupant, "origin", mid);
 +
 +	fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
 +	qprintf ("%5i point linefile\n", count+1);
 +
 +	fclose (linefile);
 +}
 +
 diff --git a/code/bspc/linux-i386.mak b/code/bspc/linux-i386.mak new file mode 100755 index 0000000..00a261d --- /dev/null +++ b/code/bspc/linux-i386.mak @@ -0,0 +1,109 @@ +#
 +# Makefile for the BSPC tool for the Gladiator Bot
 +# Intended for gcc/Linux
 +#
 +
 +ARCH=i386
 +CC=gcc
 +BASE_CFLAGS=-Dstricmp=strcasecmp
 +
 +#use these cflags to optimize it
 +CFLAGS=$(BASE_CFLAGS) -m486 -O6 -ffast-math -funroll-loops \
 +	-fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \
 +	-malign-jumps=2 -malign-functions=2 -DLINUX -DBSPC
 +#use these when debugging 
 +#CFLAGS=$(BASE_CFLAGS) -g
 +
 +LDFLAGS=-ldl -lm -lpthread
 +
 +DO_CC=$(CC) $(CFLAGS) -o $@ -c $<
 +
 +#############################################################################
 +# SETUP AND BUILD BSPC
 +#############################################################################
 +
 +.c.o:
 +	$(DO_CC)
 +
 +GAME_OBJS = \
 +	_files.o\
 +	aas_areamerging.o\
 +	aas_cfg.o\
 +	aas_create.o\
 +	aas_edgemelting.o\
 +	aas_facemerging.o\
 +	aas_file.o\
 +	aas_gsubdiv.o\
 +	aas_map.o\
 +	aas_prunenodes.o\
 +	aas_store.o\
 +	be_aas_bspc.o\
 +	../botlib/be_aas_bspq3.o\
 +	../botlib/be_aas_cluster.o\
 +	../botlib/be_aas_move.o\
 +	../botlib/be_aas_optimize.o\
 +	../botlib/be_aas_reach.o\
 +	../botlib/be_aas_sample.o\
 +	brushbsp.o\
 +	bspc.o\
 +	../qcommon/cm_load.o\
 +	../qcommon/cm_patch.o\
 +	../qcommon/cm_test.o\
 +	../qcommon/cm_trace.o\
 +	csg.o\
 +	glfile.o\
 +	l_bsp_ent.o\
 +	l_bsp_hl.o\
 +	l_bsp_q1.o\
 +	l_bsp_q2.o\
 +	l_bsp_q3.o\
 +	l_bsp_sin.o\
 +	l_cmd.o\
 +	../botlib/l_libvar.o\
 +	l_log.o\
 +	l_math.o\
 +	l_mem.o\
 +	l_poly.o\
 +	../botlib/l_precomp.o\
 +	l_qfiles.o\
 +	../botlib/l_script.o\
 +	../botlib/l_struct.o\
 +	l_threads.o\
 +	l_utils.o\
 +	leakfile.o\
 +	map.o\
 +	map_hl.o\
 +	map_q1.o\
 +	map_q2.o\
 +	map_q3.o\
 +	map_sin.o\
 +	../qcommon/md4.o\
 +	nodraw.o\
 +	portals.o\
 +	tetrahedron.o\
 +	textures.o\
 +	tree.o\
 +	../qcommon/unzip.o
 +
 +bspc$(ARCH) : $(GAME_OBJS)
 +	$(CC) $(CFLAGS) -o $@ $(GAME_OBJS) $(LDFLAGS)
 +
 +
 +#############################################################################
 +# MISC
 +#############################################################################
 +
 +clean:
 +	-rm -f $(GAME_OBJS)
 +
 +depend:
 +	gcc -MM $(GAME_OBJS:.o=.c)
 +
 +
 +install:
 +	cp bspci386 ..
 +
 +#
 +# From "make depend"
 +#
 +
 diff --git a/code/bspc/map.c b/code/bspc/map.c new file mode 100755 index 0000000..613c234 --- /dev/null +++ b/code/bspc/map.c @@ -0,0 +1,1267 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_bsp_hl.h"
 +#include "l_bsp_q1.h"
 +#include "l_bsp_q2.h"
 +#include "l_bsp_q3.h"
 +#include "l_bsp_sin.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"		//aas_bbox_t
 +#include "aas_store.h"		//AAS_MAX_BBOXES
 +#include "aas_cfg.h"
 +
 +#define Sign(x)		(x < 0 ? 1 : 0)
 +
 +int					nummapbrushes;
 +mapbrush_t			mapbrushes[MAX_MAPFILE_BRUSHES];
 +
 +int					nummapbrushsides;
 +side_t				brushsides[MAX_MAPFILE_BRUSHSIDES];
 +brush_texture_t		side_brushtextures[MAX_MAPFILE_BRUSHSIDES];
 +
 +int					nummapplanes;
 +plane_t				mapplanes[MAX_MAPFILE_PLANES];
 +int					mapplaneusers[MAX_MAPFILE_PLANES];
 +
 +#define				PLANE_HASHES	1024
 +plane_t				*planehash[PLANE_HASHES];
 +vec3_t				map_mins, map_maxs;
 +
 +#ifdef SIN
 +textureref_t		side_newrefs[MAX_MAPFILE_BRUSHSIDES];
 +#endif
 +
 +map_texinfo_t		map_texinfo[MAX_MAPFILE_TEXINFO];
 +int					map_numtexinfo;
 +int					loadedmaptype;		//loaded map type
 +
 +// undefine to make plane finding use linear sort
 +#define	USE_HASHING
 +
 +int c_boxbevels;
 +int c_edgebevels;
 +int c_areaportals;
 +int c_clipbrushes;
 +int c_squattbrushes;
 +int c_writtenbrushes;
 +
 +/*
 +=============================================================================
 +
 +PLANE FINDING
 +
 +=============================================================================
 +*/
 +
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int PlaneSignBits(vec3_t normal)
 +{
 +	int i, signbits;
 +
 +	signbits = 0;
 +	for (i = 2; i >= 0; i--)
 +	{
 +		signbits = (signbits << 1) + Sign(normal[i]);
 +	} //end for
 +	return signbits;
 +} //end of the function PlaneSignBits
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int PlaneTypeForNormal(vec3_t normal)
 +{
 +	vec_t	ax, ay, az;
 +	
 +// NOTE: should these have an epsilon around 1.0?		
 +	if (normal[0] == 1.0 || normal[0] == -1.0)
 +		return PLANE_X;
 +	if (normal[1] == 1.0 || normal[1] == -1.0)
 +		return PLANE_Y;
 +	if (normal[2] == 1.0 || normal[2] == -1.0)
 +		return PLANE_Z;
 +		
 +	ax = fabs(normal[0]);
 +	ay = fabs(normal[1]);
 +	az = fabs(normal[2]);
 +	
 +	if (ax >= ay && ax >= az)
 +		return PLANE_ANYX;
 +	if (ay >= ax && ay >= az)
 +		return PLANE_ANYY;
 +	return PLANE_ANYZ;
 +} //end of the function PlaneTypeForNormal
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +//ME NOTE: changed from 0.00001
 +#define	NORMAL_EPSILON	0.0001
 +//ME NOTE: changed from 0.01
 +#define	DIST_EPSILON	0.02
 +qboolean	PlaneEqual(plane_t *p, vec3_t normal, vec_t dist)
 +{
 +#if 1
 +	if (
 +	   fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
 +	&& fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
 +	&& fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
 +	&& fabs(p->dist - dist) < DIST_EPSILON )
 +		return true;
 +#else
 +	if (p->normal[0] == normal[0]
 +		&& p->normal[1] == normal[1]
 +		&& p->normal[2] == normal[2]
 +		&& p->dist == dist)
 +		return true;
 +#endif
 +	return false;
 +} //end of the function PlaneEqual
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddPlaneToHash(plane_t *p)
 +{
 +	int		hash;
 +
 +	hash = (int)fabs(p->dist) / 8;
 +	hash &= (PLANE_HASHES-1);
 +
 +	p->hash_chain = planehash[hash];
 +	planehash[hash] = p;
 +} //end of the function AddPlaneToHash
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int CreateNewFloatPlane (vec3_t normal, vec_t dist)
 +{
 +	plane_t	*p, temp;
 +
 +	if (VectorLength(normal) < 0.5)
 +		Error ("FloatPlane: bad normal");
 +	// create a new plane
 +	if (nummapplanes+2 > MAX_MAPFILE_PLANES)
 +		Error ("MAX_MAPFILE_PLANES");
 +
 +	p = &mapplanes[nummapplanes];
 +	VectorCopy (normal, p->normal);
 +	p->dist = dist;
 +	p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
 +	p->signbits = PlaneSignBits(p->normal);
 +
 +	VectorSubtract (vec3_origin, normal, (p+1)->normal);
 +	(p+1)->dist = -dist;
 +	(p+1)->signbits = PlaneSignBits((p+1)->normal);
 +
 +	nummapplanes += 2;
 +
 +	// allways put axial planes facing positive first
 +	if (p->type < 3)
 +	{
 +		if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
 +		{
 +			// flip order
 +			temp = *p;
 +			*p = *(p+1);
 +			*(p+1) = temp;
 +
 +			AddPlaneToHash (p);
 +			AddPlaneToHash (p+1);
 +			return nummapplanes - 1;
 +		}
 +	}
 +
 +	AddPlaneToHash (p);
 +	AddPlaneToHash (p+1);
 +	return nummapplanes - 2;
 +} //end of the function CreateNewFloatPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void SnapVector(vec3_t normal)
 +{
 +	int		i;
 +
 +	for (i=0 ; i<3 ; i++)
 +	{
 +		if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
 +		{
 +			VectorClear (normal);
 +			normal[i] = 1;
 +			break;
 +		}
 +		if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
 +		{
 +			VectorClear (normal);
 +			normal[i] = -1;
 +			break;
 +		}
 +	}
 +} //end of the function SnapVector
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void SnapPlane(vec3_t normal, vec_t *dist)
 +{
 +	SnapVector(normal);
 +
 +	if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
 +		*dist = Q_rint(*dist);
 +} //end of the function SnapPlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#ifndef USE_HASHING
 +int FindFloatPlane(vec3_t normal, vec_t dist)
 +{
 +	int i;
 +	plane_t *p;
 +
 +	SnapPlane(normal, &dist);
 +	for (i = 0, p = mapplanes; i < nummapplanes; i++, p++)
 +	{
 +		if (PlaneEqual (p, normal, dist))
 +		{
 +			mapplaneusers[i]++;
 +			return i;
 +		} //end if
 +	} //end for
 +	i = CreateNewFloatPlane (normal, dist);
 +	mapplaneusers[i]++;
 +	return i;
 +} //end of the function FindFloatPlane
 +#else
 +int FindFloatPlane (vec3_t normal, vec_t dist)
 +{
 +	int i;
 +	plane_t *p;
 +	int hash, h;
 +
 +	SnapPlane (normal, &dist);
 +	hash = (int)fabs(dist) / 8;
 +	hash &= (PLANE_HASHES-1);
 +
 +	// search the border bins as well
 +	for (i = -1; i <= 1; i++)
 +	{
 +		h = (hash+i)&(PLANE_HASHES-1);
 +		for (p = planehash[h]; p; p = p->hash_chain)
 +		{
 +			if (PlaneEqual(p, normal, dist))
 +			{
 +				mapplaneusers[p-mapplanes]++;
 +				return p - mapplanes;
 +			} //end if
 +		} //end for
 +	} //end for
 +	i = CreateNewFloatPlane (normal, dist);
 +	mapplaneusers[i]++;
 +	return i;
 +} //end of the function FindFloatPlane
 +#endif
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int PlaneFromPoints (int *p0, int *p1, int *p2)
 +{
 +	vec3_t	t1, t2, normal;
 +	vec_t	dist;
 +
 +	VectorSubtract (p0, p1, t1);
 +	VectorSubtract (p2, p1, t2);
 +	CrossProduct (t1, t2, normal);
 +	VectorNormalize (normal);
 +
 +	dist = DotProduct (p0, normal);
 +
 +	return FindFloatPlane (normal, dist);
 +} //end of the function PlaneFromPoints
 +//===========================================================================
 +// Adds any additional planes necessary to allow the brush to be expanded
 +// against axial bounding boxes
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddBrushBevels (mapbrush_t *b)
 +{
 +	int		axis, dir;
 +	int		i, j, k, l, order;
 +	side_t	sidetemp;
 +	brush_texture_t	tdtemp;
 +#ifdef SIN
 +   textureref_t trtemp;
 +#endif
 +	side_t	*s, *s2;
 +	vec3_t	normal;
 +	float	dist;
 +	winding_t	*w, *w2;
 +	vec3_t	vec, vec2;
 +	float	d;
 +
 +	//
 +	// add the axial planes
 +	//
 +	order = 0;
 +	for (axis=0 ; axis <3 ; axis++)
 +	{
 +		for (dir=-1 ; dir <= 1 ; dir+=2, order++)
 +		{
 +			// see if the plane is allready present
 +			for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
 +			{
 +				if (mapplanes[s->planenum].normal[axis] == dir)
 +					break;
 +			}
 +
 +			if (i == b->numsides)
 +			{	// add a new side
 +				if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
 +					Error ("MAX_MAP_BRUSHSIDES");
 +				nummapbrushsides++;
 +				b->numsides++;
 +				VectorClear (normal);
 +				normal[axis] = dir;
 +				if (dir == 1)
 +					dist = b->maxs[axis];
 +				else
 +					dist = -b->mins[axis];
 +				s->planenum = FindFloatPlane (normal, dist);
 +				s->texinfo = b->original_sides[0].texinfo;
 +#ifdef SIN
 +				s->lightinfo = b->original_sides[0].lightinfo;
 +#endif
 +				s->contents = b->original_sides[0].contents;
 +				s->flags |= SFL_BEVEL;
 +				c_boxbevels++;
 +			}
 +
 +			// if the plane is not in it canonical order, swap it
 +			if (i != order)
 +			{
 +				sidetemp = b->original_sides[order];
 +				b->original_sides[order] = b->original_sides[i];
 +				b->original_sides[i] = sidetemp;
 +
 +				j = b->original_sides - brushsides;
 +				tdtemp = side_brushtextures[j+order];
 +				side_brushtextures[j+order] = side_brushtextures[j+i];
 +				side_brushtextures[j+i] = tdtemp;
 +
 +#ifdef SIN
 +				trtemp = side_newrefs[j+order];
 +				side_newrefs[j+order] = side_newrefs[j+i];
 +				side_newrefs[j+i] = trtemp;
 +#endif
 +			}
 +		}
 +	}
 +
 +	//
 +	// add the edge bevels
 +	//
 +	if (b->numsides == 6)
 +		return;		// pure axial
 +
 +	// test the non-axial plane edges
 +	for (i=6 ; i<b->numsides ; i++)
 +	{
 +		s = b->original_sides + i;
 +		w = s->winding;
 +		if (!w)
 +			continue;
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			k = (j+1)%w->numpoints;
 +			VectorSubtract (w->p[j], w->p[k], vec);
 +			if (VectorNormalize (vec) < 0.5)
 +				continue;
 +			SnapVector (vec);
 +			for (k=0 ; k<3 ; k++)
 +				if ( vec[k] == -1 || vec[k] == 1)
 +					break;	// axial
 +			if (k != 3)
 +				continue;	// only test non-axial edges
 +
 +			// try the six possible slanted axials from this edge
 +			for (axis=0 ; axis <3 ; axis++)
 +			{
 +				for (dir=-1 ; dir <= 1 ; dir+=2)
 +				{
 +					// construct a plane
 +					VectorClear (vec2);
 +					vec2[axis] = dir;
 +					CrossProduct (vec, vec2, normal);
 +					if (VectorNormalize (normal) < 0.5)
 +						continue;
 +					dist = DotProduct (w->p[j], normal);
 +
 +					// if all the points on all the sides are
 +					// behind this plane, it is a proper edge bevel
 +					for (k=0 ; k<b->numsides ; k++)
 +					{
 +						// if this plane has allready been used, skip it
 +						if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]
 +							, normal, dist) )
 +							break;
 +
 +						w2 = b->original_sides[k].winding;
 +						if (!w2)
 +							continue;
 +						for (l=0 ; l<w2->numpoints ; l++)
 +						{
 +							d = DotProduct (w2->p[l], normal) - dist;
 +							if (d > 0.1)
 +								break;	// point in front
 +						}
 +						if (l != w2->numpoints)
 +							break;
 +					}
 +
 +					if (k != b->numsides)
 +						continue;	// wasn't part of the outer hull
 +					// add this plane
 +					if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
 +						Error ("MAX_MAP_BRUSHSIDES");
 +					nummapbrushsides++;
 +					s2 = &b->original_sides[b->numsides];
 +					s2->planenum = FindFloatPlane (normal, dist);
 +					s2->texinfo = b->original_sides[0].texinfo;
 +#ifdef SIN
 +					s2->lightinfo = b->original_sides[0].lightinfo;
 +#endif
 +					s2->contents = b->original_sides[0].contents;
 +					s2->flags |= SFL_BEVEL;
 +					c_edgebevels++;
 +					b->numsides++;
 +				}
 +			}
 +		}
 +	}
 +} //end of the function AddBrushBevels
 +//===========================================================================
 +// creates windigs for sides and mins / maxs for the brush
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean MakeBrushWindings(mapbrush_t *ob)
 +{
 +	int			i, j;
 +	winding_t	*w;
 +	side_t		*side;
 +	plane_t		*plane;
 +
 +	ClearBounds (ob->mins, ob->maxs);
 +
 +	for (i = 0; i < ob->numsides; i++)
 +	{
 +		plane = &mapplanes[ob->original_sides[i].planenum];
 +		w = BaseWindingForPlane(plane->normal, plane->dist);
 +		for (j = 0; j <ob->numsides && w; j++)
 +		{
 +			if (i == j) continue;
 +			if (ob->original_sides[j].flags & SFL_BEVEL) continue;
 +			plane = &mapplanes[ob->original_sides[j].planenum^1];
 +			ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
 +		}
 +
 +		side = &ob->original_sides[i];
 +		side->winding = w;
 +		if (w)
 +		{
 +			side->flags |= SFL_VISIBLE;
 +			for (j = 0; j < w->numpoints; j++)
 +				AddPointToBounds (w->p[j], ob->mins, ob->maxs);
 +		}
 +	}
 +
 +	for (i = 0; i < 3; i++)
 +	{
 +		//IDBUG: all the indexes into the mins and maxs were zero (not using i)
 +		if (ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);
 +			ob->numsides = 0; //remove the brush
 +			break;
 +		} //end if
 +		if (ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);
 +			ob->numsides = 0; //remove the brush
 +			break;
 +		} //end if
 +	} //end for
 +	return true;
 +} //end of the function MakeBrushWindings
 +//===========================================================================
 +// FIXME: currently doesn't mark all bevels
 +// NOTE: when one brush bevel is found the remaining sides of the brush
 +//       are bevels as well (when the brush isn't expanded for AAS :))
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void MarkBrushBevels(mapbrush_t *brush)
 +{
 +	int i;
 +	int we;
 +	side_t *s;
 +
 +	//check all the sides of the brush
 +	for (i = 0; i < brush->numsides; i++)
 +	{
 +		s = brush->original_sides + i;
 +		//if the side has no winding
 +		if (!s->winding)
 +		{
 +			Log_Write("MarkBrushBevels: brush %d no winding", brush->brushnum);
 +			s->flags |= SFL_BEVEL;
 +		} //end if
 +		//if the winding is tiny
 +		else if (WindingIsTiny(s->winding))
 +		{
 +			s->flags |= SFL_BEVEL;
 +			Log_Write("MarkBrushBevels: brush %d tiny winding", brush->brushnum);
 +		} //end else if
 +		//if the winding has errors
 +		else
 +		{
 +			we = WindingError(s->winding);
 +			if (we == WE_NOTENOUGHPOINTS
 +					|| we == WE_SMALLAREA
 +					|| we == WE_POINTBOGUSRANGE
 +//					|| we == WE_NONCONVEX
 +					)
 +			{
 +				Log_Write("MarkBrushBevels: brush %d %s", brush->brushnum, WindingErrorString());
 +				s->flags |= SFL_BEVEL;
 +			} //end else if
 +		} //end else
 +		if (s->flags & SFL_BEVEL)
 +		{
 +			s->flags &= ~SFL_VISIBLE;
 +			//if the side has a valid plane
 +			if (s->planenum > 0 && s->planenum < nummapplanes)
 +			{
 +				//if it is an axial plane
 +				if (mapplanes[s->planenum].type < 3) c_boxbevels++;
 +				else c_edgebevels++;
 +			} //end if
 +		} //end if
 +	} //end for
 +} //end of the function MarkBrushBevels
 +//===========================================================================
 +// returns true if the map brush already exists
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int BrushExists(mapbrush_t *brush)
 +{
 +	int i, s1, s2;
 +	side_t *side1, *side2;
 +	mapbrush_t *brush1, *brush2;
 +
 +	for (i = 0; i < nummapbrushes; i++)
 +	{
 +		brush1 = brush;
 +		brush2 = &mapbrushes[i];
 +		//compare the brushes
 +		if (brush1->entitynum != brush2->entitynum) continue;
 +		//if (brush1->contents != brush2->contents) continue;
 +		if (brush1->numsides != brush2->numsides) continue;
 +		for (s1 = 0; s1 < brush1->numsides; s1++)
 +		{
 +			side1 = brush1->original_sides + s1;
 +			//
 +			for (s2 = 0; s2 < brush2->numsides; s2++)
 +			{
 +				side2 = brush2->original_sides + s2;
 +				//
 +				if ((side1->planenum & ~1) == (side2->planenum & ~1)
 +//						&& side1->texinfo == side2->texinfo
 +//						&& side1->contents == side2->contents
 +//						&& side1->surf == side2->surf
 +					) break;
 +			} //end if
 +			if (s2 >= brush2->numsides) break;
 +		} //end for
 +		if (s1 >= brush1->numsides) return true;
 +	} //end for
 +	return false;
 +} //end of the function BrushExists
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WriteMapBrush(FILE *fp, mapbrush_t *brush, vec3_t origin)
 +{
 +	int sn, rotate, shift[2], sv, tv, planenum, p1, i, j;
 +	float scale[2], originshift[2], ang1, ang2, newdist;
 +	vec3_t vecs[2], axis[2];
 +	map_texinfo_t *ti;
 +	winding_t *w;
 +	side_t *s;
 +	plane_t *plane;
 +
 +	if (noliquids)
 +	{
 +		if (brush->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))
 +		{
 +			return true;
 +		} //end if
 +	} //end if
 +	//if the brush has no contents
 +	if (!brush->contents) return true;
 +	//print the leading {
 +	if (fprintf(fp, " { //brush %d\n", brush->brushnum) < 0) return false;
 +	//write brush sides
 +	for (sn = 0; sn < brush->numsides; sn++)
 +	{
 +		s = brush->original_sides + sn;
 +		//don't write out bevels
 +		if (!(s->flags & SFL_BEVEL))
 +		{
 +			//if the entity has an origin set
 +			if (origin[0] || origin[1] || origin[2])
 +			{
 +				newdist = mapplanes[s->planenum].dist +
 +					DotProduct(mapplanes[s->planenum].normal, origin);
 +				planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist);
 +			} //end if
 +			else
 +			{
 +				planenum = s->planenum;
 +			} //end else
 +			//always take the first plane, then flip the points if necesary
 +			plane = &mapplanes[planenum & ~1];
 +			w = BaseWindingForPlane(plane->normal, plane->dist);
 +			//
 +			for (i = 0; i < 3; i++)
 +			{
 +				for (j = 0; j < 3; j++)
 +				{
 +					if (fabs(w->p[i][j]) < 0.2) w->p[i][j] = 0;
 +					else if (fabs((int)w->p[i][j] - w->p[i][j]) < 0.3) w->p[i][j] = (int) w->p[i][j];
 +					//w->p[i][j] = (int) (w->p[i][j] + 0.2);
 +				} //end for
 +			} //end for
 +			//three non-colinear points to define the plane
 +			if (planenum & 1) p1 = 1;
 +			else p1 = 0;
 +			if (fprintf(fp,"  ( %5i %5i %5i ) ", (int)w->p[p1][0], (int)w->p[p1][1], (int)w->p[p1][2]) < 0) return false;
 +			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[!p1][0], (int)w->p[!p1][1], (int)w->p[!p1][2]) < 0) return false;
 +			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false;
 +			//free the winding
 +			FreeWinding(w);
 +			//
 +			if (s->texinfo == TEXINFO_NODE)
 +			{
 +				if (brush->contents & CONTENTS_PLAYERCLIP)
 +				{
 +					//player clip
 +					if (loadedmaptype == MAPTYPE_SIN)
 +					{
 +						if (fprintf(fp, "generic/misc/clip 0 0 0 1 1") < 0) return false;
 +					} //end if
 +					else if (loadedmaptype == MAPTYPE_QUAKE2)
 +					{	//FIXME: don't always use e1u1
 +						if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false;
 +					} //end else
 +					else if (loadedmaptype == MAPTYPE_QUAKE3)
 +					{
 +						if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false;
 +					} //end else if
 +					else
 +					{
 +						if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
 +					} //end else
 +				} //end if
 +				else if (brush->contents == CONTENTS_MONSTERCLIP)
 +				{
 +					//monster clip
 +					if (loadedmaptype == MAPTYPE_SIN)
 +					{
 +						if (fprintf(fp, "generic/misc/monster 0 0 0 1 1") < 0) return false;
 +					} //end if
 +					else if (loadedmaptype == MAPTYPE_QUAKE2)
 +					{
 +						if (fprintf(fp, "e1u1/clip_mon 0 0 0 1 1") < 0) return false;
 +					} //end else
 +					else
 +					{
 +						if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
 +					} //end else
 +				} //end else
 +				else
 +				{
 +					if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
 +					Log_Write("brush->contents = %d\n", brush->contents);
 +				} //end else
 +			} //end if
 +			else if (loadedmaptype == MAPTYPE_SIN && s->texinfo == 0)
 +			{
 +				if (brush->contents & CONTENTS_DUMMYFENCE)
 +				{
 +					if (fprintf(fp, "generic/misc/fence 0 0 0 1 1") < 0) return false;
 +				} //end if
 +				else if (brush->contents & CONTENTS_MIST)
 +				{
 +					if (fprintf(fp, "generic/misc/volumetric_base 0 0 0 1 1") < 0) return false;
 +				} //end if
 +				else //unknown so far
 +				{
 +					if (fprintf(fp, "generic/misc/red 0 0 0 1 1") < 0) return false;
 +				} //end else
 +			} //end if
 +			else if (loadedmaptype == MAPTYPE_QUAKE3)
 +			{
 +				//always use the same texture
 +				if (fprintf(fp, "e2u3/floor1_2 0 0 0 1 1 1 0 0") < 0) return false;
 +			} //end else if
 +			else
 +			{
 +				//*
 +				ti = &map_texinfo[s->texinfo];
 +				//the scaling of the texture
 +				scale[0] = 1 / VectorNormalize2(ti->vecs[0], vecs[0]);
 +				scale[1] = 1 / VectorNormalize2(ti->vecs[1], vecs[1]);
 +				//
 +				TextureAxisFromPlane(plane, axis[0], axis[1]);
 +				//calculate texture shift done by entity origin
 +				originshift[0] = DotProduct(origin, axis[0]);
 +				originshift[1] = DotProduct(origin, axis[1]);
 +				//the texture shift without origin shift
 +				shift[0] = ti->vecs[0][3] - originshift[0];
 +				shift[1] = ti->vecs[1][3] - originshift[1];
 +				//
 +				if (axis[0][0]) sv = 0;
 +				else if (axis[0][1]) sv = 1;
 +				else sv = 2;
 +				if (axis[1][0]) tv = 0;
 +				else if (axis[1][1]) tv = 1;
 +				else tv = 2;
 +				//calculate rotation of texture
 +				if (vecs[0][tv] == 0) ang1 = vecs[0][sv] > 0 ? 90.0 : -90.0;
 +				else ang1 = atan2(vecs[0][sv], vecs[0][tv]) * 180 / Q_PI;
 +				if (ang1 < 0) ang1 += 360;
 +				if (ang1 >= 360) ang1 -= 360;
 +				if (axis[0][tv] == 0) ang2 = axis[0][sv] > 0 ? 90.0 : -90.0;
 +				else ang2 = atan2(axis[0][sv], axis[0][tv]) * 180 / Q_PI;
 +				if (ang2 < 0) ang2 += 360;
 +				if (ang2 >= 360) ang2 -= 360;
 +				rotate = ang2 - ang1;
 +				if (rotate < 0) rotate += 360;
 +				if (rotate >= 360) rotate -= 360;
 +				//write the texture info
 +				if (fprintf(fp, "%s %d %d %d", ti->texture, shift[0], shift[1], rotate) < 0) return false;
 +				if (fabs(scale[0] - ((int) scale[0])) < 0.001)
 +				{
 +					if (fprintf(fp, " %d", (int) scale[0]) < 0) return false;
 +				} //end if
 +				else
 +				{
 +					if (fprintf(fp, " %4f", scale[0]) < 0) return false;
 +				} //end if
 +				if (fabs(scale[1] - ((int) scale[1])) < 0.001)
 +				{
 +					if (fprintf(fp, " %d", (int) scale[1]) < 0) return false;
 +				} //end if
 +				else
 +				{
 +					if (fprintf(fp, " %4f", scale[1]) < 0) return false;
 +				} //end else
 +				//write the extra brush side info
 +				if (loadedmaptype == MAPTYPE_QUAKE2)
 +				{
 +					if (fprintf(fp, " %ld %ld %ld", s->contents, ti->flags, ti->value) < 0) return false;
 +				} //end if
 +				//*/
 +			} //end else
 +			if (fprintf(fp, "\n") < 0) return false;
 +		} //end if
 +	} //end if
 +	if (fprintf(fp, " }\n") < 0) return false;
 +	c_writtenbrushes++;
 +	return true;
 +} //end of the function WriteMapBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WriteOriginBrush(FILE *fp, vec3_t origin)
 +{
 +	vec3_t normal;
 +	float dist;
 +	int i, s;
 +	winding_t *w;
 +
 +	if (fprintf(fp, " {\n") < 0) return false;
 +	//
 +	for (i = 0; i < 3; i++)
 +	{
 +		for (s = -1; s <= 1; s += 2)
 +		{
 +			//
 +			VectorClear(normal);
 +			normal[i] = s;
 +			dist = origin[i] * s + 16;
 +			//
 +			w = BaseWindingForPlane(normal, dist);
 +			//three non-colinear points to define the plane
 +			if (fprintf(fp,"  ( %5i %5i %5i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]) < 0) return false;
 +			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]) < 0) return false;
 +			if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false;
 +			//free the winding
 +			FreeWinding(w);
 +			//write origin texture:
 +			// CONTENTS_ORIGIN = 16777216
 +			// SURF_NODRAW = 128
 +			if (loadedmaptype == MAPTYPE_SIN)
 +			{
 +				if (fprintf(fp, "generic/misc/origin 0 0 0 1 1") < 0) return false;
 +			} //end if
 +			else if (loadedmaptype == MAPTYPE_HALFLIFE)
 +			{
 +				if (fprintf(fp, "origin 0 0 0 1 1") < 0) return false;
 +			} //end if
 +			else
 +			{
 +				if (fprintf(fp, "e1u1/origin 0 0 0 1 1") < 0) return false;
 +			} //end else
 +			//Quake2 extra brush side info
 +			if (loadedmaptype == MAPTYPE_QUAKE2)
 +			{
 +				//if (fprintf(fp, " 16777216 128 0") < 0) return false;
 +			} //end if
 +			if (fprintf(fp, "\n") < 0) return false;
 +		} //end for
 +	} //end for
 +	if (fprintf(fp, " }\n") < 0) return false;
 +	c_writtenbrushes++;
 +	return true;
 +} //end of the function WriteOriginBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +mapbrush_t *GetAreaPortalBrush(entity_t *mapent)
 +{
 +	int portalnum, bn;
 +	mapbrush_t *brush;
 +
 +	//the area portal number
 +	portalnum = mapent->areaportalnum;
 +	//find the area portal brush in the world brushes
 +	for (bn = 0; bn < nummapbrushes && portalnum; bn++)
 +	{
 +		brush = &mapbrushes[bn];
 +		//must be in world entity
 +		if (brush->entitynum == 0)
 +		{
 +			if (brush->contents & CONTENTS_AREAPORTAL)
 +			{
 +				portalnum--;
 +			} //end if
 +		} //end if
 +	} //end for
 +	if (bn < nummapbrushes)
 +	{
 +		return brush;
 +	} //end if
 +	else
 +	{
 +		Log_Print("area portal %d brush not found\n", mapent->areaportalnum);
 +		return NULL;
 +	} //end else
 +} //end of the function GetAreaPortalBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WriteMapFileSafe(FILE *fp)
 +{
 +	char key[1024], value[1024];
 +	int i, bn, entitybrushes;
 +	epair_t *ep;
 +	mapbrush_t *brush;
 +	entity_t *mapent;
 +	//vec3_t vec_origin = {0, 0, 0};
 +
 +	//
 +	if (fprintf(fp,"//=====================================================\n"
 +			"//\n"
 +			"// map file created with BSPC "BSPC_VERSION"\n"
 +			"//\n"
 +			"// BSPC is designed to decompile material in which you own the copyright\n"
 +			"// or have obtained permission to decompile from the copyright owner. Unless\n"
 +			"// you own the copyright or have permission to decompile from the copyright\n"
 +			"// owner, you may be violating copyright law and be subject to payment of\n"
 +			"// damages and other remedies. If you are uncertain about your rights, contact\n"
 +			"// your legal advisor.\n"
 +			"//\n") < 0) return false;
 +	if (loadedmaptype == MAPTYPE_SIN)
 +	{
 +		if (fprintf(fp,
 +						"// generic/misc/red is used for unknown textures\n") < 0) return false;
 +	} //end if
 +	if (fprintf(fp,"//\n"
 +						"//=====================================================\n") < 0) return false;
 +	//write out all the entities
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		mapent = &entities[i];
 +		if (!mapent->epairs)
 +		{
 +			continue;
 +		} //end if
 +		if (fprintf(fp, "{\n") < 0) return false;
 +		//
 +		if (loadedmaptype == MAPTYPE_QUAKE3)
 +		{
 +			if (!stricmp(ValueForKey(mapent, "classname"), "light"))
 +			{
 +				SetKeyValue(mapent, "light", "10000");
 +			} //end if
 +		} //end if
 +		//write epairs
 +		for (ep = mapent->epairs; ep; ep = ep->next)
 +		{
 +			strcpy(key, ep->key);
 +			StripTrailing (key);
 +			strcpy(value, ep->value);
 +			StripTrailing(value);
 +			//
 +			if (loadedmaptype == MAPTYPE_QUAKE2 ||
 +					loadedmaptype == MAPTYPE_SIN)
 +			{
 +				//don't write an origin for BSP models
 +				if (mapent->modelnum >= 0 && !strcmp(key, "origin")) continue;
 +			} //end if
 +			//don't write BSP model numbers
 +			if (mapent->modelnum >= 0 && !strcmp(key, "model") && value[0] == '*') continue;
 +			//
 +			if (fprintf(fp, " \"%s\" \"%s\"\n", key, value) < 0) return false;
 +		} //end for
 +		//
 +		if (ValueForKey(mapent, "origin")) GetVectorForKey(mapent, "origin", mapent->origin);
 +		else mapent->origin[0] = mapent->origin[1] = mapent->origin[2] = 0;
 +		//if this is an area portal entity
 +		if (!strcmp("func_areaportal", ValueForKey(mapent, "classname")))
 +		{
 +			brush = GetAreaPortalBrush(mapent);
 +			if (!brush) return false;
 +			if (!WriteMapBrush(fp, brush, mapent->origin)) return false;
 +		} //end if
 +		else
 +		{
 +			entitybrushes = false;
 +			//write brushes
 +			for (bn = 0; bn < nummapbrushes; bn++)
 +			{
 +				brush = &mapbrushes[bn];
 +				//if the brush is part of this entity
 +				if (brush->entitynum == i)
 +				{
 +					//don't write out area portal brushes in the world
 +					if (!((brush->contents & CONTENTS_AREAPORTAL) && brush->entitynum == 0))
 +					{
 +						/*
 +						if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname")))
 +						{
 +							AAS_PositionFuncRotatingBrush(mapent, brush);
 +							if (!WriteMapBrush(fp, brush, vec_origin)) return false;
 +						} //end if
 +						else //*/
 +						{
 +							if (!WriteMapBrush(fp, brush, mapent->origin)) return false;
 +						} //end else
 +						entitybrushes = true;
 +					} //end if
 +				} //end if
 +			} //end for
 +			//if the entity had brushes
 +			if (entitybrushes)
 +			{
 +				//if the entity has an origin set
 +				if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
 +				{
 +					if (!WriteOriginBrush(fp, mapent->origin)) return false;
 +				} //end if
 +			} //end if
 +		} //end else
 +		if (fprintf(fp, "}\n") < 0) return false;
 +	} //end for
 +	if (fprintf(fp, "//total of %d brushes\n", c_writtenbrushes) < 0) return false;
 +	return true;
 +} //end of the function WriteMapFileSafe
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void WriteMapFile(char *filename)
 +{
 +	FILE *fp;
 +	double start_time;
 +
 +	c_writtenbrushes = 0;
 +	//the time started
 +	start_time = I_FloatTime();
 +	//
 +	Log_Print("writing %s\n", filename);
 +	fp = fopen(filename, "wb");
 +	if (!fp)
 +	{
 +		Log_Print("can't open %s\n", filename);
 +		return;
 +	} //end if
 +	if (!WriteMapFileSafe(fp))
 +	{
 +		fclose(fp);
 +		Log_Print("error writing map file %s\n", filename);
 +		return;
 +	} //end if
 +	fclose(fp);
 +	//display creation time
 +	Log_Print("written %d brushes\n", c_writtenbrushes);
 +	Log_Print("map file written in %5.0f seconds\n", I_FloatTime() - start_time);
 +} //end of the function WriteMapFile
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintMapInfo(void)
 +{
 +	Log_Print("\n");
 +	Log_Print("%6i brushes\n", nummapbrushes);
 +	Log_Print("%6i brush sides\n", nummapbrushsides);
 +//	Log_Print("%6i clipbrushes\n", c_clipbrushes);
 +//	Log_Print("%6i total sides\n", nummapbrushsides);
 +//	Log_Print("%6i boxbevels\n", c_boxbevels);
 +//	Log_Print("%6i edgebevels\n", c_edgebevels);
 +//	Log_Print("%6i entities\n", num_entities);
 +//	Log_Print("%6i planes\n", nummapplanes);
 +//	Log_Print("%6i areaportals\n", c_areaportals);
 +//	Log_Print("%6i squatt brushes\n", c_squattbrushes);
 +//	Log_Print("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
 +//		map_maxs[0],map_maxs[1],map_maxs[2]);
 +} //end of the function PrintMapInfo
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void ResetMapLoading(void)
 +{
 +	int i;
 +	epair_t *ep, *nextep;
 +
 +	Q2_ResetMapLoading();
 +	Sin_ResetMapLoading();
 +
 +	//free all map brush side windings
 +	for (i = 0; i < nummapbrushsides; i++)
 +	{
 +		if (brushsides[i].winding)
 +		{
 +			FreeWinding(brushsides[i].winding);
 +		} //end for
 +	} //end for
 +
 +	//reset regular stuff
 +	nummapbrushes = 0;
 +	memset(mapbrushes, 0, MAX_MAPFILE_BRUSHES * sizeof(mapbrush_t));
 +	//
 +	nummapbrushsides = 0;
 +	memset(brushsides, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(side_t));
 +	memset(side_brushtextures, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(brush_texture_t));
 +	//
 +	nummapplanes = 0;
 +	memset(mapplanes, 0, MAX_MAPFILE_PLANES * sizeof(plane_t));
 +	//
 +	memset(planehash, 0, PLANE_HASHES * sizeof(plane_t *));
 +	//
 +	memset(map_texinfo, 0, MAX_MAPFILE_TEXINFO * sizeof(map_texinfo_t));
 +	map_numtexinfo = 0;
 +	//
 +	VectorClear(map_mins);
 +	VectorClear(map_maxs);
 +	//
 +	c_boxbevels = 0;
 +	c_edgebevels = 0;
 +	c_areaportals = 0;
 +	c_clipbrushes = 0;
 +	c_writtenbrushes = 0;
 +	//clear the entities
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		for (ep = entities[i].epairs; ep; ep = nextep)
 +		{
 +			nextep = ep->next;
 +			FreeMemory(ep->key);
 +			FreeMemory(ep->value);
 +			FreeMemory(ep);
 +		} //end for
 +	} //end for
 +	num_entities = 0;
 +	memset(entities, 0, MAX_MAP_ENTITIES * sizeof(entity_t));
 +} //end of the function ResetMapLoading
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#ifndef Q1_BSPVERSION
 +#define Q1_BSPVERSION	29
 +#endif
 +#ifndef HL_BSPVERSION
 +#define HL_BSPVERSION	30
 +#endif
 +
 +#define Q2_BSPHEADER				(('P'<<24)+('S'<<16)+('B'<<8)+'I')	//IBSP
 +#define Q2_BSPVERSION	38
 +
 +#define SINGAME_BSPHEADER		(('P'<<24)+('S'<<16)+('B'<<8)+'R')	//RBSP
 +#define SINGAME_BSPVERSION		1
 +
 +#define SIN_BSPHEADER			(('P'<<24)+('S'<<16)+('B'<<8)+'I')	//IBSP
 +#define SIN_BSPVERSION	41
 +
 +typedef struct
 +{
 +	int ident;
 +	int version;
 +} idheader_t;
 +
 +int LoadMapFromBSP(struct quakefile_s *qf)
 +{
 +	idheader_t idheader;
 +
 +	if (ReadQuakeFile(qf, &idheader, 0, sizeof(idheader_t)) != sizeof(idheader_t))
 +	{
 +		return false;
 +	} //end if
 +
 +	idheader.ident = LittleLong(idheader.ident);
 +	idheader.version = LittleLong(idheader.version);
 +	//Quake3 BSP file
 +	if (idheader.ident == Q3_BSP_IDENT && idheader.version == Q3_BSP_VERSION)
 +	{
 +		ResetMapLoading();
 +		Q3_LoadMapFromBSP(qf);
 +		Q3_FreeMaxBSP();
 +	} //end if
 +	//Quake2 BSP file
 +	else if (idheader.ident == Q2_BSPHEADER && idheader.version == Q2_BSPVERSION)
 +	{
 +		ResetMapLoading();
 +		Q2_AllocMaxBSP();
 +		Q2_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
 +		Q2_FreeMaxBSP();
 +	} //endif
 +	//Sin BSP file
 +	else if ((idheader.ident == SIN_BSPHEADER && idheader.version == SIN_BSPVERSION) ||
 +				//the dorks gave the same format another ident and verions
 +				(idheader.ident == SINGAME_BSPHEADER && idheader.version == SINGAME_BSPVERSION))
 +	{
 +		ResetMapLoading();
 +		Sin_AllocMaxBSP();
 +		Sin_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
 +		Sin_FreeMaxBSP();
 +	} //end if
 +	//the Quake1 bsp files don't have a ident only a version
 +	else if (idheader.ident == Q1_BSPVERSION)
 +	{
 +		ResetMapLoading();
 +		Q1_AllocMaxBSP();
 +		Q1_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
 +		Q1_FreeMaxBSP();
 +	} //end if
 +	//Half-Life also only uses a version number
 +	else if (idheader.ident == HL_BSPVERSION)
 +	{
 +		ResetMapLoading();
 +		HL_AllocMaxBSP();
 +		HL_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
 +		HL_FreeMaxBSP();
 +	} //end if
 +	else
 +	{
 +		Error("unknown BSP format %c%c%c%c, version %d\n",
 +										(idheader.ident & 0xFF),
 +										((idheader.ident >> 8) & 0xFF),
 +										((idheader.ident >> 16) & 0xFF),
 +										((idheader.ident >> 24) & 0xFF), idheader.version);
 +		return false;
 +	} //end if
 +	//
 +	return true;
 +} //end of the function LoadMapFromBSP
 diff --git a/code/bspc/map_hl.c b/code/bspc/map_hl.c new file mode 100755 index 0000000..7e6037b --- /dev/null +++ b/code/bspc/map_hl.c @@ -0,0 +1,1114 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_bsp_hl.h"
 +#include "aas_map.h"			//AAS_CreateMapBrushes
 +
 +int hl_numbrushes;
 +int hl_numclipbrushes;
 +
 +//#define HL_PRINT
 +#define HLCONTENTS
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int HL_TextureContents(char *name)
 +{
 +	if (!Q_strncasecmp (name, "sky",3))
 +		return CONTENTS_SOLID;
 +
 +	if (!Q_strncasecmp(name+1,"!lava",5))
 +		return CONTENTS_LAVA;
 +
 +	if (!Q_strncasecmp(name+1,"!slime",6))
 +		return CONTENTS_SLIME;
 +
 +	/*
 +	if (!Q_strncasecmp (name, "!cur_90",7))
 +		return CONTENTS_CURRENT_90;
 +	if (!Q_strncasecmp (name, "!cur_0",6))
 +		return CONTENTS_CURRENT_0;
 +	if (!Q_strncasecmp (name, "!cur_270",8))
 +		return CONTENTS_CURRENT_270;
 +	if (!Q_strncasecmp (name, "!cur_180",8))
 +		return CONTENTS_CURRENT_180;
 +	if (!Q_strncasecmp (name, "!cur_up",7))
 +		return CONTENTS_CURRENT_UP;
 +	if (!Q_strncasecmp (name, "!cur_dwn",8))
 +		return CONTENTS_CURRENT_DOWN;
 +	//*/
 +	if (name[0] == '!')
 +		return CONTENTS_WATER;
 +	/*
 +	if (!Q_strncasecmp (name, "origin",6))
 +		return CONTENTS_ORIGIN;
 +	if (!Q_strncasecmp (name, "clip",4))
 +		return CONTENTS_CLIP;
 +	if( !Q_strncasecmp( name, "translucent", 11 ) )
 +		return CONTENTS_TRANSLUCENT;
 +	if( name[0] == '@' )
 +		return CONTENTS_TRANSLUCENT;
 +	//*/
 +
 +	return CONTENTS_SOLID;
 +} //end of the function HL_TextureContents
 +//===========================================================================
 +// Generates two new brushes, leaving the original
 +// unchanged
 +//
 +// modified for Half-Life because there are quite a lot of tiny node leaves
 +// in the Half-Life bsps
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void HL_SplitBrush(bspbrush_t *brush, int planenum, int nodenum,
 +						 bspbrush_t **front, bspbrush_t **back)
 +{
 +	bspbrush_t *b[2];
 +	int i, j;
 +	winding_t *w, *cw[2], *midwinding;
 +	plane_t *plane, *plane2;
 +	side_t *s, *cs;
 +	float d, d_front, d_back;
 +
 +	*front = *back = NULL;
 +	plane = &mapplanes[planenum];
 +
 +	// check all points
 +	d_front = d_back = 0;
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (!w)
 +			continue;
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			d = DotProduct (w->p[j], plane->normal) - plane->dist;
 +			if (d > 0 && d > d_front)
 +				d_front = d;
 +			if (d < 0 && d < d_back)
 +				d_back = d;
 +		} //end for
 +	} //end for
 +
 +	if (d_front < 0.1) // PLANESIDE_EPSILON)
 +	{	// only on back
 +		*back = CopyBrush (brush);
 +		Log_Print("HL_SplitBrush: only on back\n");
 +		return;
 +	} //end if
 +	if (d_back > -0.1) // PLANESIDE_EPSILON)
 +	{	// only on front
 +		*front = CopyBrush (brush);
 +		Log_Print("HL_SplitBrush: only on front\n");
 +		return;
 +	} //end if
 +
 +	// create a new winding from the split plane
 +
 +	w = BaseWindingForPlane (plane->normal, plane->dist);
 +	for (i = 0; i < brush->numsides && w; i++)
 +	{
 +		plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
 +		ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
 +	} //end for
 +
 +	if (!w || WindingIsTiny(w))
 +	{	// the brush isn't really split
 +		int		side;
 +
 +		Log_Print("HL_SplitBrush: no split winding\n");
 +		side = BrushMostlyOnSide (brush, plane);
 +		if (side == PSIDE_FRONT)
 +			*front = CopyBrush (brush);
 +		if (side == PSIDE_BACK)
 +			*back = CopyBrush (brush);
 +		return;
 +	}
 +
 +	if (WindingIsHuge(w))
 +	{
 +		Log_Print("HL_SplitBrush: WARNING huge split winding\n");
 +	} //end of
 +
 +	midwinding = w;
 +
 +	// split it for real
 +
 +	for (i = 0; i < 2; i++)
 +	{
 +		b[i] = AllocBrush (brush->numsides+1);
 +		b[i]->original = brush->original;
 +	} //end for
 +
 +	// split all the current windings
 +
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		s = &brush->sides[i];
 +		w = s->winding;
 +		if (!w)
 +			continue;
 +		ClipWindingEpsilon (w, plane->normal, plane->dist,
 +			0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
 +		for (j=0 ; j<2 ; j++)
 +		{
 +			if (!cw[j])
 +				continue;
 +#if 0
 +			if (WindingIsTiny (cw[j]))
 +			{
 +				FreeWinding (cw[j]);
 +				continue;
 +			}
 +#endif
 +			cs = &b[j]->sides[b[j]->numsides];
 +			b[j]->numsides++;
 +			*cs = *s;
 +//			cs->planenum = s->planenum;
 +//			cs->texinfo = s->texinfo;
 +//			cs->visible = s->visible;
 +//			cs->original = s->original;
 +			cs->winding = cw[j];
 +			cs->flags &= ~SFL_TESTED;
 +		} //end for
 +	} //end for
 +
 +
 +	// see if we have valid polygons on both sides
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		BoundBrush (b[i]);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096)
 +			{
 +				Log_Print("HL_SplitBrush: bogus brush after clip\n");
 +				break;
 +			} //end if
 +		} //end for
 +
 +		if (b[i]->numsides < 3 || j < 3)
 +		{
 +			FreeBrush (b[i]);
 +			b[i] = NULL;
 +			Log_Print("HL_SplitBrush: numsides < 3\n");
 +		} //end if
 +	} //end for
 +
 +	if ( !(b[0] && b[1]) )
 +	{
 +		if (!b[0] && !b[1])
 +			Log_Print("HL_SplitBrush: split removed brush\n");
 +		else
 +			Log_Print("HL_SplitBrush: split not on both sides\n");
 +		if (b[0])
 +		{
 +			FreeBrush (b[0]);
 +			*front = CopyBrush (brush);
 +		} //end if
 +		if (b[1])
 +		{
 +			FreeBrush (b[1]);
 +			*back = CopyBrush (brush);
 +		} //end if
 +		return;
 +	} //end if
 +
 +	// add the midwinding to both sides
 +	for (i = 0; i < 2; i++)
 +	{
 +		cs = &b[i]->sides[b[i]->numsides];
 +		b[i]->numsides++;
 +
 +		cs->planenum = planenum^i^1;
 +		cs->texinfo = 0;
 +		//store the node number in the surf to find the texinfo later on
 +		cs->surf = nodenum;
 +		//
 +		cs->flags &= ~SFL_VISIBLE;
 +		cs->flags &= ~SFL_TESTED;
 +		if (i==0)
 +			cs->winding = CopyWinding (midwinding);
 +		else
 +			cs->winding = midwinding;
 +	} //end for
 +
 +
 +{
 +	vec_t v1;
 +	int i;
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		v1 = BrushVolume (b[i]);
 +		if (v1 < 1)
 +		{
 +			FreeBrush (b[i]);
 +			b[i] = NULL;
 +			Log_Print("HL_SplitBrush: tiny volume after clip\n");
 +		} //end if
 +	} //end for
 +} //*/
 +
 +	*front = b[0];
 +	*back = b[1];
 +} //end of the function HL_SplitBrush
 +//===========================================================================
 +// returns true if the tree starting at nodenum has only solid leaves
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int HL_SolidTree_r(int nodenum)
 +{
 +	if (nodenum < 0)
 +	{
 +		switch(hl_dleafs[(-nodenum) - 1].contents)
 +		{
 +			case HL_CONTENTS_EMPTY:
 +			{
 +				return false;
 +			} //end case
 +			case HL_CONTENTS_SOLID:
 +#ifdef HLCONTENTS
 +			case HL_CONTENTS_CLIP:
 +#endif //HLCONTENTS
 +			case HL_CONTENTS_SKY:
 +#ifdef HLCONTENTS
 +			case HL_CONTENTS_TRANSLUCENT:
 +#endif //HLCONTENTS
 +			{
 +				return true;
 +			} //end case
 +			case HL_CONTENTS_WATER:
 +			case HL_CONTENTS_SLIME:
 +			case HL_CONTENTS_LAVA:
 +#ifdef HLCONTENTS
 +			//these contents should not be found in the BSP
 +			case HL_CONTENTS_ORIGIN:
 +			case HL_CONTENTS_CURRENT_0:
 +			case HL_CONTENTS_CURRENT_90:
 +			case HL_CONTENTS_CURRENT_180:
 +			case HL_CONTENTS_CURRENT_270:
 +			case HL_CONTENTS_CURRENT_UP:
 +			case HL_CONTENTS_CURRENT_DOWN:
 +#endif //HLCONTENTS
 +			default:
 +			{
 +				return false;
 +			} //end default
 +		} //end switch
 +		return false;
 +	} //end if
 +	if (!HL_SolidTree_r(hl_dnodes[nodenum].children[0])) return false;
 +	if (!HL_SolidTree_r(hl_dnodes[nodenum].children[1])) return false;
 +	return true;
 +} //end of the function HL_SolidTree_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *HL_CreateBrushes_r(bspbrush_t *brush, int nodenum)
 +{
 +	int planenum;
 +	bspbrush_t *front, *back;
 +	hl_dleaf_t *leaf;
 +
 +	//if it is a leaf
 +	if (nodenum < 0)
 +	{
 +		leaf = &hl_dleafs[(-nodenum) - 1];
 +		if (leaf->contents != HL_CONTENTS_EMPTY)
 +		{
 +#ifdef HL_PRINT
 +			qprintf("\r%5i", ++hl_numbrushes);
 +#endif //HL_PRINT
 +		} //end if
 +		switch(leaf->contents)
 +		{
 +			case HL_CONTENTS_EMPTY:
 +			{
 +				FreeBrush(brush);
 +				return NULL;
 +			} //end case
 +			case HL_CONTENTS_SOLID:
 +#ifdef HLCONTENTS
 +			case HL_CONTENTS_CLIP:
 +#endif //HLCONTENTS
 +			case HL_CONTENTS_SKY:
 +#ifdef HLCONTENTS
 +			case HL_CONTENTS_TRANSLUCENT:
 +#endif //HLCONTENTS
 +			{
 +				brush->side = CONTENTS_SOLID;
 +				return brush;
 +			} //end case
 +			case HL_CONTENTS_WATER:
 +			{
 +				brush->side = CONTENTS_WATER;
 +				return brush;
 +			} //end case
 +			case HL_CONTENTS_SLIME:
 +			{
 +				brush->side = CONTENTS_SLIME;
 +				return brush;
 +			} //end case
 +			case HL_CONTENTS_LAVA:
 +			{
 +				brush->side = CONTENTS_LAVA;
 +				return brush;
 +			} //end case
 +#ifdef HLCONTENTS
 +			//these contents should not be found in the BSP
 +			case HL_CONTENTS_ORIGIN:
 +			case HL_CONTENTS_CURRENT_0:
 +			case HL_CONTENTS_CURRENT_90:
 +			case HL_CONTENTS_CURRENT_180:
 +			case HL_CONTENTS_CURRENT_270:
 +			case HL_CONTENTS_CURRENT_UP:
 +			case HL_CONTENTS_CURRENT_DOWN:
 +			{
 +				Error("HL_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents);
 +				return NULL;
 +			} //end case
 +#endif //HLCONTENTS
 +			default:
 +			{
 +				Error("HL_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents);
 +				return NULL;
 +			} //end default
 +		} //end switch
 +		return NULL;
 +	} //end if
 +	//if the rest of the tree is solid
 +	/*if (HL_SolidTree_r(nodenum))
 +	{
 +		brush->side = CONTENTS_SOLID;
 +		return brush;
 +	} //end if*/
 +	//
 +	planenum = hl_dnodes[nodenum].planenum;
 +	planenum = FindFloatPlane(hl_dplanes[planenum].normal, hl_dplanes[planenum].dist);
 +	//split the brush with the node plane
 +	HL_SplitBrush(brush, planenum, nodenum, &front, &back);
 +	//free the original brush
 +	FreeBrush(brush);
 +	//every node must split the brush in two
 +	if (!front || !back)
 +	{
 +		Log_Print("HL_CreateBrushes_r: WARNING node not splitting brush\n");
 +		//return NULL;
 +	} //end if
 +	//create brushes recursively
 +	if (front) front = HL_CreateBrushes_r(front, hl_dnodes[nodenum].children[0]);
 +	if (back) back = HL_CreateBrushes_r(back, hl_dnodes[nodenum].children[1]);
 +	//link the brushes if possible and return them
 +	if (front)
 +	{
 +		for (brush = front; brush->next; brush = brush->next);
 +		brush->next = back;
 +		return front;
 +	} //end if
 +	else
 +	{
 +		return back;
 +	} //end else
 +} //end of the function HL_CreateBrushes_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *HL_CreateBrushesFromBSP(int modelnum)
 +{
 +	bspbrush_t *brushlist;
 +	bspbrush_t *brush;
 +	hl_dnode_t *headnode;
 +	vec3_t mins, maxs;
 +	int i;
 +
 +	//
 +	headnode = &hl_dnodes[hl_dmodels[modelnum].headnode[0]];
 +	//get the mins and maxs of the world
 +	VectorCopy(headnode->mins, mins);
 +	VectorCopy(headnode->maxs, maxs);
 +	//enlarge these mins and maxs
 +	for (i = 0; i < 3; i++)
 +	{
 +		mins[i] -= 8;
 +		maxs[i] += 8;
 +	} //end for
 +	//NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs
 +	AddPointToBounds(mins, map_mins, map_maxs);
 +	AddPointToBounds(maxs, map_mins, map_maxs);
 +	//
 +	if (!modelnum)
 +	{
 +		Log_Print("brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n",
 +							map_mins[0], map_mins[1], map_mins[2],
 +							map_maxs[0], map_maxs[1], map_maxs[2]);
 +	} //end if
 +	//create one huge brush containing the whole world
 +	brush = BrushFromBounds(mins, maxs);
 +	VectorCopy(mins, brush->mins);
 +	VectorCopy(maxs, brush->maxs);
 +	//
 +#ifdef HL_PRINT
 +	qprintf("creating Half-Life brushes\n");
 +	qprintf("%5d brushes", hl_numbrushes = 0);
 +#endif //HL_PRINT
 +	//create the brushes
 +	brushlist = HL_CreateBrushes_r(brush, hl_dmodels[modelnum].headnode[0]);
 +	//
 +#ifdef HL_PRINT
 +	qprintf("\n");
 +#endif //HL_PRINT
 +	//now we've got a list with brushes!
 +	return brushlist;
 +} //end of the function HL_CreateBrushesFromBSP
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *HL_MergeBrushes(bspbrush_t *brushlist, int modelnum)
 +{
 +	int nummerges, merged;
 +	bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist;
 +	bspbrush_t *lastb2;
 +
 +	if (!brushlist) return NULL;
 +
 +	if (!modelnum) qprintf("%5d brushes merged", nummerges = 0);
 +	do
 +	{
 +		for (tail = brushlist; tail; tail = tail->next)
 +		{
 +			if (!tail->next) break;
 +		} //end for
 +		merged = 0;
 +		newbrushlist = NULL;
 +		for (b1 = brushlist; b1; b1 = brushlist)
 +		{
 +			lastb2 = b1;
 +			for (b2 = b1->next; b2; b2 = b2->next)
 +			{
 +				//can't merge brushes with different contents
 +				if (b1->side != b2->side) newbrush = NULL;
 +				else newbrush = TryMergeBrushes(b1, b2);
 +				//if a merged brush is created
 +				if (newbrush)
 +				{
 +					//copy the brush contents
 +					newbrush->side = b1->side;
 +					//add the new brush to the end of the list
 +					tail->next = newbrush;
 +					//remove the second brush from the list
 +					lastb2->next = b2->next;
 +					//remove the first brush from the list
 +					brushlist = brushlist->next;
 +					//free the merged brushes
 +					FreeBrush(b1);
 +					FreeBrush(b2);
 +					//get a new tail brush
 +					for (tail = brushlist; tail; tail = tail->next)
 +					{
 +						if (!tail->next) break;
 +					} //end for
 +					merged++;
 +					if (!modelnum) qprintf("\r%5d", nummerges++);
 +					break;
 +				} //end if
 +				lastb2 = b2;
 +			} //end for
 +			//if b1 can't be merged with any of the other brushes
 +			if (!b2)
 +			{
 +				brushlist = brushlist->next;
 +				//keep b1
 +				b1->next = newbrushlist;
 +				newbrushlist = b1;
 +			} //end else
 +		} //end for
 +		brushlist = newbrushlist;
 +	} while(merged);
 +	if (!modelnum) qprintf("\n");
 +	return newbrushlist;
 +} //end of the function HL_MergeBrushes
 +//===========================================================================
 +// returns the amount the face and the winding have overlap
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +float HL_FaceOnWinding(hl_dface_t *face, winding_t *winding)
 +{
 +	int i, edgenum, side;
 +	float dist, area;
 +	hl_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	winding_t *w;
 +
 +	//
 +	w = CopyWinding(winding);
 +	memcpy(&plane, &hl_dplanes[face->planenum], sizeof(hl_dplane_t));
 +	//check on which side of the plane the face is
 +	if (face->side)
 +	{
 +		VectorNegate(plane.normal, plane.normal);
 +		plane.dist = -plane.dist;
 +	} //end if
 +	for (i = 0; i < face->numedges && w; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = hl_dsurfedges[face->firstedge + i];
 +		side = edgenum > 0;
 +		//if the face plane is flipped
 +		v1 = hl_dvertexes[hl_dedges[abs(edgenum)].v[side]].point;
 +		v2 = hl_dvertexes[hl_dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing out of the face
 +		VectorSubtract(v1, v2, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON
 +	} //end for
 +	if (w)
 +	{
 +		area = WindingArea(w);
 +		FreeWinding(w);
 +		return area;
 +	} //end if
 +	return 0;
 +} //end of the function HL_FaceOnWinding
 +//===========================================================================
 +// returns a list with brushes created by splitting the given brush with
 +// planes that go through the face edges and are orthogonal to the face plane
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *HL_SplitBrushWithFace(bspbrush_t *brush, hl_dface_t *face)
 +{
 +	int i, edgenum, side, planenum, splits;
 +	float dist;
 +	hl_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	bspbrush_t *front, *back, *brushlist;
 +
 +	memcpy(&plane, &hl_dplanes[face->planenum], sizeof(hl_dplane_t));
 +	//check on which side of the plane the face is
 +	if (face->side)
 +	{
 +		VectorNegate(plane.normal, plane.normal);
 +		plane.dist = -plane.dist;
 +	} //end if
 +	splits = 0;
 +	brushlist = NULL;
 +	for (i = 0; i < face->numedges; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = hl_dsurfedges[face->firstedge + i];
 +		side = edgenum > 0;
 +		//if the face plane is flipped
 +		v1 = hl_dvertexes[hl_dedges[abs(edgenum)].v[side]].point;
 +		v2 = hl_dvertexes[hl_dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing out of the face
 +		VectorSubtract(v1, v2, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		planenum = FindFloatPlane(normal, dist);
 +		//split the current brush
 +		SplitBrush(brush, planenum, &front, &back);
 +		//if there is a back brush just put it in the list
 +		if (back)
 +		{
 +			//copy the brush contents
 +			back->side = brush->side;
 +			//
 +			back->next = brushlist;
 +			brushlist = back;
 +			splits++;
 +		} //end if
 +		if (!front)
 +		{
 +			Log_Print("HL_SplitBrushWithFace: no new brush\n");
 +			FreeBrushList(brushlist);
 +			return NULL;
 +		} //end if
 +		//copy the brush contents
 +		front->side = brush->side;
 +		//continue splitting the front brush
 +		brush = front;
 +	} //end for
 +	if (!splits)
 +	{
 +		FreeBrush(front);
 +		return NULL;
 +	} //end if
 +	front->next = brushlist;
 +	brushlist = front;
 +	return brushlist;
 +} //end of the function HL_SplitBrushWithFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *HL_TextureBrushes(bspbrush_t *brushlist, int modelnum)
 +{
 +	float area, largestarea;
 +	int i, n, texinfonum, sn, numbrushes, ofs;
 +	int bestfacenum, sidenodenum;
 +	side_t *side;
 +	hl_dmiptexlump_t *miptexlump;
 +	hl_miptex_t *miptex;
 +	bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend;
 +	vec_t defaultvec[4] = {1, 0, 0, 0};
 +
 +	if (!modelnum) qprintf("texturing brushes\n");
 +	if (!modelnum) qprintf("%5d brushes", numbrushes = 0);
 +	//get a pointer to the last brush in the list
 +	for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next)
 +	{
 +		if (!brushlistend->next) break;
 +	} //end for
 +	//there's no previous brush when at the start of the list
 +	prevbrush = NULL;
 +	//go over the brush list
 +	for (brush = brushlist; brush; brush = nextbrush)
 +	{
 +		nextbrush = brush->next;
 +		//find a texinfo for every brush side
 +		for (sn = 0; sn < brush->numsides; sn++)
 +		{
 +			side = &brush->sides[sn];
 +			//
 +			if (side->flags & SFL_TEXTURED) continue;
 +			//number of the node that created this brush side
 +			sidenodenum = side->surf;	//see midwinding in HL_SplitBrush
 +			//no face found yet
 +			bestfacenum = -1;
 +			//minimum face size
 +			largestarea = 1;
 +			//if optimizing the texture placement and not going for the
 +			//least number of brushes
 +			if (!lessbrushes)
 +			{
 +				for (i = 0; i < hl_numfaces; i++)
 +				{
 +					//the face must be in the same plane as the node plane that created
 +					//this brush side
 +					if (hl_dfaces[i].planenum == hl_dnodes[sidenodenum].planenum)
 +					{
 +						//get the area the face and the brush side overlap
 +						area = HL_FaceOnWinding(&hl_dfaces[i], side->winding);
 +						//if this face overlaps the brush side winding more than previous faces
 +						if (area > largestarea)
 +						{
 +							//if there already was a face for texturing this brush side with
 +							//a different texture
 +							if (bestfacenum >= 0 &&
 +									(hl_dfaces[bestfacenum].texinfo != hl_dfaces[i].texinfo))
 +							{
 +								//split the brush to fit the texture
 +								newbrushes = HL_SplitBrushWithFace(brush, &hl_dfaces[i]);
 +								//if new brushes where created
 +								if (newbrushes)
 +								{
 +									//remove the current brush from the list
 +									if (prevbrush) prevbrush->next = brush->next;
 +									else brushlist = brush->next;
 +									if (brushlistend == brush)
 +									{
 +										brushlistend = prevbrush;
 +										nextbrush = newbrushes;
 +									} //end if
 +									//add the new brushes to the end of the list
 +									if (brushlistend) brushlistend->next = newbrushes;
 +									else brushlist = newbrushes;
 +									//free the current brush
 +									FreeBrush(brush);
 +									//don't forget about the prevbrush pointer at the bottom of
 +									//the outer loop
 +									brush = prevbrush;
 +									//find the end of the list
 +									for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next)
 +									{
 +										if (!brushlistend->next) break;
 +									} //end for
 +									break;
 +								} //end if
 +								else
 +								{
 +									Log_Write("brush %d: no real texture split", numbrushes);
 +								} //end else
 +							} //end if
 +							else
 +							{
 +								//best face for texturing this brush side
 +								bestfacenum = i;
 +							} //end else
 +						} //end if
 +					} //end if
 +				} //end for
 +				//if the brush was split the original brush is removed
 +				//and we just continue with the next one in the list
 +				if (i < hl_numfaces) break;
 +			} //end if
 +			else
 +			{
 +				//find the face with the largest overlap with this brush side
 +				//for texturing the brush side
 +				for (i = 0; i < hl_numfaces; i++)
 +				{
 +					//the face must be in the same plane as the node plane that created
 +					//this brush side
 +					if (hl_dfaces[i].planenum == hl_dnodes[sidenodenum].planenum)
 +					{
 +						//get the area the face and the brush side overlap
 +						area = HL_FaceOnWinding(&hl_dfaces[i], side->winding);
 +						//if this face overlaps the brush side winding more than previous faces
 +						if (area > largestarea)
 +						{
 +							largestarea = area;
 +							bestfacenum = i;
 +						} //end if
 +					} //end if
 +				} //end for
 +			} //end else
 +			//if a face was found for texturing this brush side
 +			if (bestfacenum >= 0)
 +			{
 +				//set the MAP texinfo values
 +				texinfonum = hl_dfaces[bestfacenum].texinfo;
 +				for (n = 0; n < 4; n++)
 +				{
 +					map_texinfo[texinfonum].vecs[0][n] = hl_texinfo[texinfonum].vecs[0][n];
 +					map_texinfo[texinfonum].vecs[1][n] = hl_texinfo[texinfonum].vecs[1][n];
 +				} //end for
 +				//make sure the two vectors aren't of zero length otherwise use the default
 +				//vector to prevent a divide by zero in the map writing
 +				if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01)
 +					memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec));
 +				if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01)
 +					memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec));
 +				//
 +				map_texinfo[texinfonum].flags = hl_texinfo[texinfonum].flags;
 +				map_texinfo[texinfonum].value = 0; //HL_ and HL texinfos don't have a value
 +				//the mip texture
 +				miptexlump = (hl_dmiptexlump_t *) hl_dtexdata;
 +				ofs = miptexlump->dataofs[hl_texinfo[texinfonum].miptex];
 +				if ( ofs > hl_texdatasize ) {
 +					ofs = miptexlump->dataofs[0];
 +				}
 +				miptex = (hl_miptex_t *)((byte *)miptexlump + ofs );
 +				//get the mip texture name
 +				strcpy(map_texinfo[texinfonum].texture, miptex->name);
 +				//no animations in Quake1 and Half-Life mip textures
 +				map_texinfo[texinfonum].nexttexinfo = -1;
 +				//store the texinfo number
 +				side->texinfo = texinfonum;
 +				//
 +				if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum;
 +				//this side is textured
 +				side->flags |= SFL_TEXTURED;
 +			} //end if
 +			else
 +			{
 +				//no texture for this side
 +				side->texinfo = TEXINFO_NODE;
 +				//this side is textured
 +				side->flags |= SFL_TEXTURED;
 +			} //end if
 +		} //end for
 +		//
 +		if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes);
 +		//previous brush in the list
 +		prevbrush = brush;
 +	} //end for
 +	if (!modelnum) qprintf("\n");
 +	//return the new list with brushes
 +	return brushlist;
 +} //end of the function HL_TextureBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void HL_FixContentsTextures(bspbrush_t *brushlist)
 +{
 +	int i, texinfonum;
 +	bspbrush_t *brush;
 +
 +	for (brush = brushlist; brush; brush = brush->next)
 +	{
 +		//only fix the textures of water, slime and lava brushes
 +		if (brush->side != CONTENTS_WATER &&
 +			brush->side != CONTENTS_SLIME &&
 +			brush->side != CONTENTS_LAVA) continue;
 +		//
 +		for (i = 0; i < brush->numsides; i++)
 +		{
 +			texinfonum = brush->sides[i].texinfo;
 +			if (HL_TextureContents(map_texinfo[texinfonum].texture) == brush->side) break;
 +		} //end for
 +		//if no specific contents texture was found
 +		if (i >= brush->numsides)
 +		{
 +			texinfonum = -1;
 +			for (i = 0; i < map_numtexinfo; i++)
 +			{
 +				if (HL_TextureContents(map_texinfo[i].texture) == brush->side)
 +				{
 +					texinfonum = i;
 +					break;
 +				} //end if
 +			} //end for
 +		} //end if
 +		//
 +		if (texinfonum >= 0)
 +		{
 +			//give all the brush sides this contents texture
 +			for (i = 0; i < brush->numsides; i++)
 +			{
 +				brush->sides[i].texinfo = texinfonum;
 +			} //end for
 +		} //end if
 +		else Log_Print("brush contents %d with wrong textures\n", brush->side);
 +		//
 +	} //end for
 +	/*
 +	for (brush = brushlist; brush; brush = brush->next)
 +	{
 +		//give all the brush sides this contents texture
 +		for (i = 0; i < brush->numsides; i++)
 +		{
 +			if (HL_TextureContents(map_texinfo[texinfonum].texture) != brush->side)
 +			{
 +				Error("brush contents %d with wrong contents textures %s\n", brush->side,
 +							HL_TextureContents(map_texinfo[texinfonum].texture));
 +			} //end if
 +		} //end for
 +	} //end for*/
 +} //end of the function HL_FixContentsTextures
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void HL_BSPBrushToMapBrush(bspbrush_t *bspbrush, entity_t *mapent)
 +{
 +	mapbrush_t *mapbrush;
 +	side_t *side;
 +	int i, besttexinfo;
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +	Error ("nummapbrushes == MAX_MAPFILE_BRUSHES");
 +
 +	mapbrush = &mapbrushes[nummapbrushes];
 +	mapbrush->original_sides = &brushsides[nummapbrushsides];
 +	mapbrush->entitynum = mapent - entities;
 +	mapbrush->brushnum = nummapbrushes - mapent->firstbrush;
 +	mapbrush->leafnum = -1;
 +	mapbrush->numsides = 0;
 +
 +	besttexinfo = TEXINFO_NODE;
 +	for (i = 0; i < bspbrush->numsides; i++)
 +	{
 +		if (!bspbrush->sides[i].winding) continue;
 +		//
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		side = &brushsides[nummapbrushsides];
 +		//the contents of the bsp brush is stored in the side variable
 +		side->contents = bspbrush->side;
 +		side->surf = 0;
 +		side->planenum = bspbrush->sides[i].planenum;
 +		side->texinfo = bspbrush->sides[i].texinfo;
 +		if (side->texinfo != TEXINFO_NODE)
 +		{
 +			//this brush side is textured
 +			side->flags |= SFL_TEXTURED;
 +			besttexinfo = side->texinfo;
 +		} //end if
 +		//
 +		nummapbrushsides++;
 +		mapbrush->numsides++;
 +	} //end for
 +	//
 +	if (besttexinfo == TEXINFO_NODE)
 +	{
 +		mapbrush->numsides = 0;
 +		hl_numclipbrushes++;
 +		return;
 +	} //end if
 +	//set the texinfo for all the brush sides without texture
 +	for (i = 0; i < mapbrush->numsides; i++)
 +	{
 +		if (mapbrush->original_sides[i].texinfo == TEXINFO_NODE)
 +		{
 +			mapbrush->original_sides[i].texinfo = besttexinfo;
 +		} //end if
 +	} //end for
 +	//contents of the brush
 +	mapbrush->contents = bspbrush->side;
 +	//
 +	if (create_aas)
 +	{
 +		//create the AAS brushes from this brush, add brush bevels
 +		AAS_CreateMapBrushes(mapbrush, mapent, true);
 +		return;
 +	} //end if
 +	//create windings for sides and bounds for brush
 +	MakeBrushWindings(mapbrush);
 +	//add brush bevels
 +	AddBrushBevels(mapbrush);
 +	//a new brush has been created
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +} //end of the function HL_BSPBrushToMapBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void HL_CreateMapBrushes(entity_t *mapent, int modelnum)
 +{
 +	bspbrush_t *brushlist, *brush, *nextbrush;
 +	int i;
 +
 +	//create brushes from the model BSP tree
 +	brushlist = HL_CreateBrushesFromBSP(modelnum);
 +	//texture the brushes and split them when necesary
 +	brushlist = HL_TextureBrushes(brushlist, modelnum);
 +	//fix the contents textures of all brushes
 +	HL_FixContentsTextures(brushlist);
 +	//
 +	if (!nobrushmerge)
 +	{
 +		brushlist = HL_MergeBrushes(brushlist, modelnum);
 +		//brushlist = HL_MergeBrushes(brushlist, modelnum);
 +	} //end if
 +	//
 +	if (!modelnum) qprintf("converting brushes to map brushes\n");
 +	if (!modelnum) qprintf("%5d brushes", i = 0);
 +	for (brush = brushlist; brush; brush = nextbrush)
 +	{
 +		nextbrush = brush->next;
 +		HL_BSPBrushToMapBrush(brush, mapent);
 +		brush->next = NULL;
 +		FreeBrush(brush);
 +		if (!modelnum) qprintf("\r%5d", ++i);
 +	} //end for
 +	if (!modelnum) qprintf("\n");
 +} //end of the function HL_CreateMapBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void HL_ResetMapLoading(void)
 +{
 +} //end of the function HL_ResetMapLoading
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void HL_LoadMapFromBSP(char *filename, int offset, int length)
 +{
 +	int i, modelnum;
 +	char *model, *classname;
 +
 +	Log_Print("-- HL_LoadMapFromBSP --\n");
 +	//loaded map type
 +	loadedmaptype = MAPTYPE_HALFLIFE;
 +	//
 +	qprintf("loading map from %s at %d\n", filename, offset);
 +	//load the Half-Life BSP file
 +	HL_LoadBSPFile(filename, offset, length);
 +	//
 +	hl_numclipbrushes = 0;
 +	//parse the entities from the BSP
 +	HL_ParseEntities();
 +	//clear the map mins and maxs
 +	ClearBounds(map_mins, map_maxs);
 +	//
 +	qprintf("creating Half-Life brushes\n");
 +	if (lessbrushes) qprintf("creating minimum number of brushes\n");
 +	else qprintf("placing textures correctly\n");
 +	//
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		entities[i].firstbrush = nummapbrushes;
 +		entities[i].numbrushes = 0;
 +		//
 +		classname = ValueForKey(&entities[i], "classname");
 +		if (classname && !strcmp(classname, "worldspawn"))
 +		{
 +			modelnum = 0;
 +		} //end if
 +		else
 +		{
 +			//
 +			model = ValueForKey(&entities[i], "model");
 +			if (!model || *model != '*') continue;
 +			model++;
 +			modelnum = atoi(model);
 +		} //end else
 +		//create map brushes for the entity
 +		HL_CreateMapBrushes(&entities[i], modelnum);
 +	} //end for
 +	//
 +	qprintf("%5d map brushes\n", nummapbrushes);
 +	qprintf("%5d clip brushes\n", hl_numclipbrushes);
 +} //end of the function HL_LoadMapFromBSP
 diff --git a/code/bspc/map_q1.c b/code/bspc/map_q1.c new file mode 100755 index 0000000..772a358 --- /dev/null +++ b/code/bspc/map_q1.c @@ -0,0 +1,1174 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_bsp_q1.h"
 +#include "aas_map.h"			//AAS_CreateMapBrushes
 +
 +int q1_numbrushes;
 +int q1_numclipbrushes;
 +
 +//#define Q1_PRINT
 +
 +//===========================================================================
 +// water, slime and lava brush textures names always start with a *
 +// followed by the type: "slime", "lava" or otherwise water
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Q1_TextureContents(char *name)
 +{
 +	if (!Q_strcasecmp(name, "clip")) return CONTENTS_SOLID;
 +	if (name[0] == '*')
 +	{
 +		if (!Q_strncasecmp(name+1,"lava",4)) return CONTENTS_LAVA;
 +		else if (!Q_strncasecmp(name+1,"slime",5)) return CONTENTS_SLIME;
 +		else return CONTENTS_WATER;
 +	} //end if
 +	else if (!Q_strncasecmp(name, "sky", 3)) return CONTENTS_SOLID;
 +	else return CONTENTS_SOLID;
 +} //end of the function Q1_TextureContents
 +//===========================================================================
 +// Generates two new brushes, leaving the original
 +// unchanged
 +//
 +// modified for Half-Life because there are quite a lot of tiny node leaves
 +// in the Half-Life bsps
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_SplitBrush(bspbrush_t *brush, int planenum, int nodenum,
 +						 bspbrush_t **front, bspbrush_t **back)
 +{
 +	bspbrush_t *b[2];
 +	int i, j;
 +	winding_t *w, *cw[2], *midwinding;
 +	plane_t *plane, *plane2;
 +	side_t *s, *cs;
 +	float d, d_front, d_back;
 +
 +	*front = *back = NULL;
 +	plane = &mapplanes[planenum];
 +
 +	// check all points
 +	d_front = d_back = 0;
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		w = brush->sides[i].winding;
 +		if (!w)
 +			continue;
 +		for (j=0 ; j<w->numpoints ; j++)
 +		{
 +			d = DotProduct (w->p[j], plane->normal) - plane->dist;
 +			if (d > 0 && d > d_front)
 +				d_front = d;
 +			if (d < 0 && d < d_back)
 +				d_back = d;
 +		} //end for
 +	} //end for
 +
 +	if (d_front < 0.1) // PLANESIDE_EPSILON)
 +	{	// only on back
 +		*back = CopyBrush (brush);
 +		Log_Print("Q1_SplitBrush: only on back\n");
 +		return;
 +	} //end if
 +	if (d_back > -0.1) // PLANESIDE_EPSILON)
 +	{	// only on front
 +		*front = CopyBrush (brush);
 +		Log_Print("Q1_SplitBrush: only on front\n");
 +		return;
 +	} //end if
 +
 +	// create a new winding from the split plane
 +
 +	w = BaseWindingForPlane (plane->normal, plane->dist);
 +	for (i = 0; i < brush->numsides && w; i++)
 +	{
 +		plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
 +		ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
 +	} //end for
 +
 +	if (!w || WindingIsTiny(w))
 +	{	// the brush isn't really split
 +		int		side;
 +
 +		Log_Print("Q1_SplitBrush: no split winding\n");
 +		side = BrushMostlyOnSide (brush, plane);
 +		if (side == PSIDE_FRONT)
 +			*front = CopyBrush (brush);
 +		if (side == PSIDE_BACK)
 +			*back = CopyBrush (brush);
 +		return;
 +	}
 +
 +	if (WindingIsHuge(w))
 +	{
 +		Log_Print("Q1_SplitBrush: WARNING huge split winding\n");
 +	} //end of
 +
 +	midwinding = w;
 +
 +	// split it for real
 +
 +	for (i = 0; i < 2; i++)
 +	{
 +		b[i] = AllocBrush (brush->numsides+1);
 +		b[i]->original = brush->original;
 +	} //end for
 +
 +	// split all the current windings
 +
 +	for (i=0 ; i<brush->numsides ; i++)
 +	{
 +		s = &brush->sides[i];
 +		w = s->winding;
 +		if (!w)
 +			continue;
 +		ClipWindingEpsilon (w, plane->normal, plane->dist,
 +			0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
 +		for (j=0 ; j<2 ; j++)
 +		{
 +			if (!cw[j])
 +				continue;
 +#if 0
 +			if (WindingIsTiny (cw[j]))
 +			{
 +				FreeWinding (cw[j]);
 +				continue;
 +			}
 +#endif
 +			cs = &b[j]->sides[b[j]->numsides];
 +			b[j]->numsides++;
 +			*cs = *s;
 +//			cs->planenum = s->planenum;
 +//			cs->texinfo = s->texinfo;
 +//			cs->visible = s->visible;
 +//			cs->original = s->original;
 +			cs->winding = cw[j];
 +			cs->flags &= ~SFL_TESTED;
 +		} //end for
 +	} //end for
 +
 +
 +	// see if we have valid polygons on both sides
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		BoundBrush (b[i]);
 +		for (j=0 ; j<3 ; j++)
 +		{
 +			if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096)
 +			{
 +				Log_Print("Q1_SplitBrush: bogus brush after clip\n");
 +				break;
 +			} //end if
 +		} //end for
 +
 +		if (b[i]->numsides < 3 || j < 3)
 +		{
 +			FreeBrush (b[i]);
 +			b[i] = NULL;
 +			Log_Print("Q1_SplitBrush: numsides < 3\n");
 +		} //end if
 +	} //end for
 +
 +	if ( !(b[0] && b[1]) )
 +	{
 +		if (!b[0] && !b[1])
 +			Log_Print("Q1_SplitBrush: split removed brush\n");
 +		else
 +			Log_Print("Q1_SplitBrush: split not on both sides\n");
 +		if (b[0])
 +		{
 +			FreeBrush (b[0]);
 +			*front = CopyBrush (brush);
 +		} //end if
 +		if (b[1])
 +		{
 +			FreeBrush (b[1]);
 +			*back = CopyBrush (brush);
 +		} //end if
 +		return;
 +	} //end if
 +
 +	// add the midwinding to both sides
 +	for (i = 0; i < 2; i++)
 +	{
 +		cs = &b[i]->sides[b[i]->numsides];
 +		b[i]->numsides++;
 +
 +		cs->planenum = planenum^i^1;
 +		cs->texinfo = 0;
 +		//store the node number in the surf to find the texinfo later on
 +		cs->surf = nodenum;
 +		//
 +		cs->flags &= ~SFL_VISIBLE;
 +		cs->flags &= ~SFL_TESTED;
 +		cs->flags &= ~SFL_TEXTURED;
 +		if (i==0)
 +			cs->winding = CopyWinding (midwinding);
 +		else
 +			cs->winding = midwinding;
 +	} //end for
 +
 +
 +{
 +	vec_t v1;
 +	int i;
 +
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		v1 = BrushVolume (b[i]);
 +		if (v1 < 1)
 +		{
 +			FreeBrush (b[i]);
 +			b[i] = NULL;
 +			Log_Print("Q1_SplitBrush: tiny volume after clip\n");
 +		} //end if
 +	} //end for
 +} //*/
 +
 +	*front = b[0];
 +	*back = b[1];
 +} //end of the function Q1_SplitBrush
 +//===========================================================================
 +// returns true if the tree starting at nodenum has only solid leaves
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Q1_SolidTree_r(int nodenum)
 +{
 +	if (nodenum < 0)
 +	{
 +		switch(q1_dleafs[(-nodenum) - 1].contents)
 +		{
 +			case Q1_CONTENTS_EMPTY:
 +			{
 +				return false;
 +			} //end case
 +			case Q1_CONTENTS_SOLID:
 +#ifdef HLCONTENTS
 +			case Q1_CONTENTS_CLIP:
 +#endif HLCONTENTS
 +			case Q1_CONTENTS_SKY:
 +#ifdef HLCONTENTS
 +			case Q1_CONTENTS_TRANSLUCENT:
 +#endif HLCONTENTS
 +			{
 +				return true;
 +			} //end case
 +			case Q1_CONTENTS_WATER:
 +			case Q1_CONTENTS_SLIME:
 +			case Q1_CONTENTS_LAVA:
 +#ifdef HLCONTENTS
 +			//these contents should not be found in the BSP
 +			case Q1_CONTENTS_ORIGIN:
 +			case Q1_CONTENTS_CURRENT_0:
 +			case Q1_CONTENTS_CURRENT_90:
 +			case Q1_CONTENTS_CURRENT_180:
 +			case Q1_CONTENTS_CURRENT_270:
 +			case Q1_CONTENTS_CURRENT_UP:
 +			case Q1_CONTENTS_CURRENT_DOWN:
 +#endif HLCONTENTS
 +			default:
 +			{
 +				return false;
 +			} //end default
 +		} //end switch
 +		return false;
 +	} //end if
 +	if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[0])) return false;
 +	if (!Q1_SolidTree_r(q1_dnodes[nodenum].children[1])) return false;
 +	return true;
 +} //end of the function Q1_SolidTree_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *Q1_CreateBrushes_r(bspbrush_t *brush, int nodenum)
 +{
 +	int planenum;
 +	bspbrush_t *front, *back;
 +	q1_dleaf_t *leaf;
 +
 +	//if it is a leaf
 +	if (nodenum < 0)
 +	{
 +		leaf = &q1_dleafs[(-nodenum) - 1];
 +		if (leaf->contents != Q1_CONTENTS_EMPTY)
 +		{
 +#ifdef Q1_PRINT
 +			qprintf("\r%5i", ++q1_numbrushes);
 +#endif //Q1_PRINT
 +		} //end if
 +		switch(leaf->contents)
 +		{
 +			case Q1_CONTENTS_EMPTY:
 +			{
 +				FreeBrush(brush);
 +				return NULL;
 +			} //end case
 +			case Q1_CONTENTS_SOLID:
 +#ifdef HLCONTENTS
 +			case Q1_CONTENTS_CLIP:
 +#endif HLCONTENTS
 +			case Q1_CONTENTS_SKY:
 +#ifdef HLCONTENTS
 +			case Q1_CONTENTS_TRANSLUCENT:
 +#endif HLCONTENTS
 +			{
 +				brush->side = CONTENTS_SOLID;
 +				return brush;
 +			} //end case
 +			case Q1_CONTENTS_WATER:
 +			{
 +				brush->side = CONTENTS_WATER;
 +				return brush;
 +			} //end case
 +			case Q1_CONTENTS_SLIME:
 +			{
 +				brush->side = CONTENTS_SLIME;
 +				return brush;
 +			} //end case
 +			case Q1_CONTENTS_LAVA:
 +			{
 +				brush->side = CONTENTS_LAVA;
 +				return brush;
 +			} //end case
 +#ifdef HLCONTENTS
 +			//these contents should not be found in the BSP
 +			case Q1_CONTENTS_ORIGIN:
 +			case Q1_CONTENTS_CURRENT_0:
 +			case Q1_CONTENTS_CURRENT_90:
 +			case Q1_CONTENTS_CURRENT_180:
 +			case Q1_CONTENTS_CURRENT_270:
 +			case Q1_CONTENTS_CURRENT_UP:
 +			case Q1_CONTENTS_CURRENT_DOWN:
 +			{
 +				Error("Q1_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents);
 +				return NULL;
 +			} //end case
 +#endif HLCONTENTS
 +			default:
 +			{
 +				Error("Q1_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents);
 +				return NULL;
 +			} //end default
 +		} //end switch
 +		return NULL;
 +	} //end if
 +	//if the rest of the tree is solid
 +	/*if (Q1_SolidTree_r(nodenum))
 +	{
 +		brush->side = CONTENTS_SOLID;
 +		return brush;
 +	} //end if*/
 +	//
 +	planenum = q1_dnodes[nodenum].planenum;
 +	planenum = FindFloatPlane(q1_dplanes[planenum].normal, q1_dplanes[planenum].dist);
 +	//split the brush with the node plane
 +	Q1_SplitBrush(brush, planenum, nodenum, &front, &back);
 +	//free the original brush
 +	FreeBrush(brush);
 +	//every node must split the brush in two
 +	if (!front || !back)
 +	{
 +		Log_Print("Q1_CreateBrushes_r: WARNING node not splitting brush\n");
 +		//return NULL;
 +	} //end if
 +	//create brushes recursively
 +	if (front) front = Q1_CreateBrushes_r(front, q1_dnodes[nodenum].children[0]);
 +	if (back) back = Q1_CreateBrushes_r(back, q1_dnodes[nodenum].children[1]);
 +	//link the brushes if possible and return them
 +	if (front)
 +	{
 +		for (brush = front; brush->next; brush = brush->next);
 +		brush->next = back;
 +		return front;
 +	} //end if
 +	else
 +	{
 +		return back;
 +	} //end else
 +} //end of the function Q1_CreateBrushes_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *Q1_CreateBrushesFromBSP(int modelnum)
 +{
 +	bspbrush_t *brushlist;
 +	bspbrush_t *brush;
 +	q1_dnode_t *headnode;
 +	vec3_t mins, maxs;
 +	int i;
 +
 +	//
 +	headnode = &q1_dnodes[q1_dmodels[modelnum].headnode[0]];
 +	//get the mins and maxs of the world
 +	VectorCopy(headnode->mins, mins);
 +	VectorCopy(headnode->maxs, maxs);
 +	//enlarge these mins and maxs
 +	for (i = 0; i < 3; i++)
 +	{
 +		mins[i] -= 8;
 +		maxs[i] += 8;
 +	} //end for
 +	//NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs
 +	AddPointToBounds(mins, map_mins, map_maxs);
 +	AddPointToBounds(maxs, map_mins, map_maxs);
 +	//
 +	if (!modelnum)
 +	{
 +		Log_Print("brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n",
 +							map_mins[0], map_mins[1], map_mins[2],
 +							map_maxs[0], map_maxs[1], map_maxs[2]);
 +	} //end if
 +	//create one huge brush containing the whole world
 +	brush = BrushFromBounds(mins, maxs);
 +	VectorCopy(mins, brush->mins);
 +	VectorCopy(maxs, brush->maxs);
 +	//
 +#ifdef Q1_PRINT
 +	qprintf("creating Quake brushes\n");
 +	qprintf("%5d brushes", q1_numbrushes = 0);
 +#endif //Q1_PRINT
 +	//create the brushes
 +	brushlist = Q1_CreateBrushes_r(brush, q1_dmodels[modelnum].headnode[0]);
 +	//
 +#ifdef Q1_PRINT
 +	qprintf("\n");
 +#endif //Q1_PRINT
 +	//now we've got a list with brushes!
 +	return brushlist;
 +} //end of the function Q1_CreateBrushesFromBSP
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +q1_dleaf_t *Q1_PointInLeaf(int startnode, vec3_t point)
 +{
 +	int nodenum;
 +	vec_t	dist;
 +	q1_dnode_t *node;
 +	q1_dplane_t *plane;
 +
 +	nodenum = startnode;
 +	while (nodenum >= 0)
 +	{
 +		node = &q1_dnodes[nodenum];
 +		plane = &q1_dplanes[node->planenum];
 +		dist = DotProduct(point, plane->normal) - plane->dist;
 +		if (dist > 0)
 +			nodenum = node->children[0];
 +		else
 +			nodenum = node->children[1];
 +	} //end while
 +
 +	return &q1_dleafs[-nodenum - 1];
 +} //end of the function Q1_PointInLeaf
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +float Q1_FaceArea(q1_dface_t *face)
 +{
 +	int i;
 +	float total;
 +	vec_t *v;
 +	vec3_t d1, d2, cross;
 +	q1_dedge_t *edge;
 +
 +	edge = &q1_dedges[face->firstedge];
 +	v = q1_dvertexes[edge->v[0]].point;
 +
 +	total = 0;
 +	for (i = 1; i < face->numedges - 1; i++)
 +	{
 +		edge = &q1_dedges[face->firstedge + i];
 +		VectorSubtract(q1_dvertexes[edge->v[0]].point, v, d1);
 +		VectorSubtract(q1_dvertexes[edge->v[1]].point, v, d2);
 +		CrossProduct(d1, d2, cross);
 +		total += 0.5 * VectorLength(cross);
 +	} //end for
 +	return total;
 +} //end of the function AAS_FaceArea
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_FacePlane(q1_dface_t *face, vec3_t normal, float *dist)
 +{
 +	vec_t *v1, *v2, *v3;
 +	vec3_t vec1, vec2;
 +	int side, edgenum;
 +
 +	edgenum = q1_dsurfedges[face->firstedge];
 +	side = edgenum < 0;
 +	v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point;
 +	v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
 +	edgenum = q1_dsurfedges[face->firstedge+1];
 +	side = edgenum < 0;
 +	v3 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
 +	//
 +	VectorSubtract(v2, v1, vec1);
 +	VectorSubtract(v3, v1, vec2);
 +
 +	CrossProduct(vec1, vec2, normal);
 +	VectorNormalize(normal);
 +	*dist = DotProduct(v1, normal);
 +} //end of the function Q1_FacePlane
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *Q1_MergeBrushes(bspbrush_t *brushlist, int modelnum)
 +{
 +	int nummerges, merged;
 +	bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist;
 +	bspbrush_t *lastb2;
 +
 +	if (!brushlist) return NULL;
 +
 +	if (!modelnum) qprintf("%5d brushes merged", nummerges = 0);
 +	do
 +	{
 +		for (tail = brushlist; tail; tail = tail->next)
 +		{
 +			if (!tail->next) break;
 +		} //end for
 +		merged = 0;
 +		newbrushlist = NULL;
 +		for (b1 = brushlist; b1; b1 = brushlist)
 +		{
 +			lastb2 = b1;
 +			for (b2 = b1->next; b2; b2 = b2->next)
 +			{
 +				//can't merge brushes with different contents
 +				if (b1->side != b2->side) newbrush = NULL;
 +				else newbrush = TryMergeBrushes(b1, b2);
 +				//if a merged brush is created
 +				if (newbrush)
 +				{
 +					//copy the brush contents
 +					newbrush->side = b1->side;
 +					//add the new brush to the end of the list
 +					tail->next = newbrush;
 +					//remove the second brush from the list
 +					lastb2->next = b2->next;
 +					//remove the first brush from the list
 +					brushlist = brushlist->next;
 +					//free the merged brushes
 +					FreeBrush(b1);
 +					FreeBrush(b2);
 +					//get a new tail brush
 +					for (tail = brushlist; tail; tail = tail->next)
 +					{
 +						if (!tail->next) break;
 +					} //end for
 +					merged++;
 +					if (!modelnum) qprintf("\r%5d", nummerges++);
 +					break;
 +				} //end if
 +				lastb2 = b2;
 +			} //end for
 +			//if b1 can't be merged with any of the other brushes
 +			if (!b2)
 +			{
 +				brushlist = brushlist->next;
 +				//keep b1
 +				b1->next = newbrushlist;
 +				newbrushlist = b1;
 +			} //end else
 +		} //end for
 +		brushlist = newbrushlist;
 +	} while(merged);
 +	if (!modelnum) qprintf("\n");
 +	return newbrushlist;
 +} //end of the function Q1_MergeBrushes
 +//===========================================================================
 +// returns the amount the face and the winding overlap
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +float Q1_FaceOnWinding(q1_dface_t *face, winding_t *winding)
 +{
 +	int i, edgenum, side;
 +	float dist, area;
 +	q1_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	winding_t *w;
 +
 +	//
 +	w = CopyWinding(winding);
 +	memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t));
 +	//check on which side of the plane the face is
 +	if (face->side)
 +	{
 +		VectorNegate(plane.normal, plane.normal);
 +		plane.dist = -plane.dist;
 +	} //end if
 +	for (i = 0; i < face->numedges && w; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = q1_dsurfedges[face->firstedge + i];
 +		side = edgenum > 0;
 +		//if the face plane is flipped
 +		v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point;
 +		v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing out of the face
 +		VectorSubtract(v1, v2, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		ChopWindingInPlace(&w, normal, dist, 0.9); //CLIP_EPSILON
 +	} //end for
 +	if (w)
 +	{
 +		area = WindingArea(w);
 +		FreeWinding(w);
 +		return area;
 +	} //end if
 +	return 0;
 +} //end of the function Q1_FaceOnWinding
 +//===========================================================================
 +// returns a list with brushes created by splitting the given brush with
 +// planes that go through the face edges and are orthogonal to the face plane
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *Q1_SplitBrushWithFace(bspbrush_t *brush, q1_dface_t *face)
 +{
 +	int i, edgenum, side, planenum, splits;
 +	float dist;
 +	q1_dplane_t plane;
 +	vec_t *v1, *v2;
 +	vec3_t normal, edgevec;
 +	bspbrush_t *front, *back, *brushlist;
 +
 +	memcpy(&plane, &q1_dplanes[face->planenum], sizeof(q1_dplane_t));
 +	//check on which side of the plane the face is
 +	if (face->side)
 +	{
 +		VectorNegate(plane.normal, plane.normal);
 +		plane.dist = -plane.dist;
 +	} //end if
 +	splits = 0;
 +	brushlist = NULL;
 +	for (i = 0; i < face->numedges; i++)
 +	{
 +		//get the first and second vertex of the edge
 +		edgenum = q1_dsurfedges[face->firstedge + i];
 +		side = edgenum > 0;
 +		//if the face plane is flipped
 +		v1 = q1_dvertexes[q1_dedges[abs(edgenum)].v[side]].point;
 +		v2 = q1_dvertexes[q1_dedges[abs(edgenum)].v[!side]].point;
 +		//create a plane through the edge vector, orthogonal to the face plane
 +		//and with the normal vector pointing out of the face
 +		VectorSubtract(v1, v2, edgevec);
 +		CrossProduct(edgevec, plane.normal, normal);
 +		VectorNormalize(normal);
 +		dist = DotProduct(normal, v1);
 +		//
 +		planenum = FindFloatPlane(normal, dist);
 +		//split the current brush
 +		SplitBrush(brush, planenum, &front, &back);
 +		//if there is a back brush just put it in the list
 +		if (back)
 +		{
 +			//copy the brush contents
 +			back->side = brush->side;
 +			//
 +			back->next = brushlist;
 +			brushlist = back;
 +			splits++;
 +		} //end if
 +		if (!front)
 +		{
 +			Log_Print("Q1_SplitBrushWithFace: no new brush\n");
 +			FreeBrushList(brushlist);
 +			return NULL;
 +		} //end if
 +		//copy the brush contents
 +		front->side = brush->side;
 +		//continue splitting the front brush
 +		brush = front;
 +	} //end for
 +	if (!splits)
 +	{
 +		FreeBrush(front);
 +		return NULL;
 +	} //end if
 +	front->next = brushlist;
 +	brushlist = front;
 +	return brushlist;
 +} //end of the function Q1_SplitBrushWithFace
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +bspbrush_t *Q1_TextureBrushes(bspbrush_t *brushlist, int modelnum)
 +{
 +	float area, largestarea;
 +	int i, n, texinfonum, sn, numbrushes, ofs;
 +	int bestfacenum, sidenodenum;
 +	side_t *side;
 +	q1_dmiptexlump_t *miptexlump;
 +	q1_miptex_t *miptex;
 +	bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend;
 +	vec_t defaultvec[4] = {1, 0, 0, 0};
 +
 +	if (!modelnum) qprintf("texturing brushes\n");
 +	if (!modelnum) qprintf("%5d brushes", numbrushes = 0);
 +	//get a pointer to the last brush in the list
 +	for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next)
 +	{
 +		if (!brushlistend->next) break;
 +	} //end for
 +	//there's no previous brush when at the start of the list
 +	prevbrush = NULL;
 +	//go over the brush list
 +	for (brush = brushlist; brush; brush = nextbrush)
 +	{
 +		nextbrush = brush->next;
 +		//find a texinfo for every brush side
 +		for (sn = 0; sn < brush->numsides; sn++)
 +		{
 +			side = &brush->sides[sn];
 +			//
 +			if (side->flags & SFL_TEXTURED) continue;
 +			//number of the node that created this brush side
 +			sidenodenum = side->surf;	//see midwinding in Q1_SplitBrush
 +			//no face found yet
 +			bestfacenum = -1;
 +			//minimum face size
 +			largestarea = 1;
 +			//if optimizing the texture placement and not going for the
 +			//least number of brushes
 +			if (!lessbrushes)
 +			{
 +				for (i = 0; i < q1_numfaces; i++)
 +				{
 +					//the face must be in the same plane as the node plane that created
 +					//this brush side
 +					if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum)
 +					{
 +						//get the area the face and the brush side overlap
 +						area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding);
 +						//if this face overlaps the brush side winding more than previous faces
 +						if (area > largestarea)
 +						{
 +							//if there already was a face for texturing this brush side with
 +							//a different texture
 +							if (bestfacenum >= 0 &&
 +									(q1_dfaces[bestfacenum].texinfo != q1_dfaces[i].texinfo))
 +							{
 +								//split the brush to fit the texture
 +								newbrushes = Q1_SplitBrushWithFace(brush, &q1_dfaces[i]);
 +								//if new brushes where created
 +								if (newbrushes)
 +								{
 +									//remove the current brush from the list
 +									if (prevbrush) prevbrush->next = brush->next;
 +									else brushlist = brush->next;
 +									if (brushlistend == brush)
 +									{
 +										brushlistend = prevbrush;
 +										nextbrush = newbrushes;
 +									} //end if
 +									//add the new brushes to the end of the list
 +									if (brushlistend) brushlistend->next = newbrushes;
 +									else brushlist = newbrushes;
 +									//free the current brush
 +									FreeBrush(brush);
 +									//don't forget about the prevbrush pointer at the bottom of
 +									//the outer loop
 +									brush = prevbrush;
 +									//find the end of the list
 +									for (brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next)
 +									{
 +										if (!brushlistend->next) break;
 +									} //end for
 +									break;
 +								} //end if
 +								else
 +								{
 +									Log_Write("brush %d: no real texture split", numbrushes);
 +								} //end else
 +							} //end if
 +							else
 +							{
 +								//best face for texturing this brush side
 +								bestfacenum = i;
 +							} //end else
 +						} //end if
 +					} //end if
 +				} //end for
 +				//if the brush was split the original brush is removed
 +				//and we just continue with the next one in the list
 +				if (i < q1_numfaces) break;
 +			} //end if
 +			else
 +			{
 +				//find the face with the largest overlap with this brush side
 +				//for texturing the brush side
 +				for (i = 0; i < q1_numfaces; i++)
 +				{
 +					//the face must be in the same plane as the node plane that created
 +					//this brush side
 +					if (q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum)
 +					{
 +						//get the area the face and the brush side overlap
 +						area = Q1_FaceOnWinding(&q1_dfaces[i], side->winding);
 +						//if this face overlaps the brush side winding more than previous faces
 +						if (area > largestarea)
 +						{
 +							largestarea = area;
 +							bestfacenum = i;
 +						} //end if
 +					} //end if
 +				} //end for
 +			} //end else
 +			//if a face was found for texturing this brush side
 +			if (bestfacenum >= 0)
 +			{
 +				//set the MAP texinfo values
 +				texinfonum = q1_dfaces[bestfacenum].texinfo;
 +				for (n = 0; n < 4; n++)
 +				{
 +					map_texinfo[texinfonum].vecs[0][n] = q1_texinfo[texinfonum].vecs[0][n];
 +					map_texinfo[texinfonum].vecs[1][n] = q1_texinfo[texinfonum].vecs[1][n];
 +				} //end for
 +				//make sure the two vectors aren't of zero length otherwise use the default
 +				//vector to prevent a divide by zero in the map writing
 +				if (VectorLength(map_texinfo[texinfonum].vecs[0]) < 0.01)
 +					memcpy(map_texinfo[texinfonum].vecs[0], defaultvec, sizeof(defaultvec));
 +				if (VectorLength(map_texinfo[texinfonum].vecs[1]) < 0.01)
 +					memcpy(map_texinfo[texinfonum].vecs[1], defaultvec, sizeof(defaultvec));
 +				//
 +				map_texinfo[texinfonum].flags = q1_texinfo[texinfonum].flags;
 +				map_texinfo[texinfonum].value = 0; //Q1 and HL texinfos don't have a value
 +				//the mip texture
 +				miptexlump = (q1_dmiptexlump_t *) q1_dtexdata;
 +				ofs = miptexlump->dataofs[q1_texinfo[texinfonum].miptex];
 +				if ( ofs > q1_texdatasize ) {
 +					ofs = miptexlump->dataofs[0];
 +				}
 +				miptex = (q1_miptex_t *)((byte *)miptexlump + ofs);
 +				//get the mip texture name
 +				strcpy(map_texinfo[texinfonum].texture, miptex->name);
 +				//no animations in Quake1 and Half-Life mip textures
 +				map_texinfo[texinfonum].nexttexinfo = -1;
 +				//store the texinfo number
 +				side->texinfo = texinfonum;
 +				//
 +				if (texinfonum > map_numtexinfo) map_numtexinfo = texinfonum;
 +				//this side is textured
 +				side->flags |= SFL_TEXTURED;
 +			} //end if
 +			else
 +			{
 +				//no texture for this side
 +				side->texinfo = TEXINFO_NODE;
 +				//this side is textured
 +				side->flags |= SFL_TEXTURED;
 +			} //end if
 +		} //end for
 +		//
 +		if (!modelnum && prevbrush != brush) qprintf("\r%5d", ++numbrushes);
 +		//previous brush in the list
 +		prevbrush = brush;
 +	} //end for
 +	if (!modelnum) qprintf("\n");
 +	//return the new list with brushes
 +	return brushlist;
 +} //end of the function Q1_TextureBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_FixContentsTextures(bspbrush_t *brushlist)
 +{
 +	int i, texinfonum;
 +	bspbrush_t *brush;
 +
 +	for (brush = brushlist; brush; brush = brush->next)
 +	{
 +		//only fix the textures of water, slime and lava brushes
 +		if (brush->side != CONTENTS_WATER &&
 +			brush->side != CONTENTS_SLIME &&
 +			brush->side != CONTENTS_LAVA) continue;
 +		//
 +		for (i = 0; i < brush->numsides; i++)
 +		{
 +			texinfonum = brush->sides[i].texinfo;
 +			if (Q1_TextureContents(map_texinfo[texinfonum].texture) == brush->side) break;
 +		} //end for
 +		//if no specific contents texture was found
 +		if (i >= brush->numsides)
 +		{
 +			texinfonum = -1;
 +			for (i = 0; i < map_numtexinfo; i++)
 +			{
 +				if (Q1_TextureContents(map_texinfo[i].texture) == brush->side)
 +				{
 +					texinfonum = i;
 +					break;
 +				} //end if
 +			} //end for
 +		} //end if
 +		//
 +		if (texinfonum >= 0)
 +		{
 +			//give all the brush sides this contents texture
 +			for (i = 0; i < brush->numsides; i++)
 +			{
 +				brush->sides[i].texinfo = texinfonum;
 +			} //end for
 +		} //end if
 +		else Log_Print("brush contents %d with wrong textures\n", brush->side);
 +		//
 +	} //end for
 +	/*
 +	for (brush = brushlist; brush; brush = brush->next)
 +	{
 +		//give all the brush sides this contents texture
 +		for (i = 0; i < brush->numsides; i++)
 +		{
 +			if (Q1_TextureContents(map_texinfo[texinfonum].texture) != brush->side)
 +			{
 +				Error("brush contents %d with wrong contents textures %s\n", brush->side,
 +							Q1_TextureContents(map_texinfo[texinfonum].texture));
 +			} //end if
 +		} //end for
 +	} //end for*/
 +} //end of the function Q1_FixContentsTextures
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_BSPBrushToMapBrush(bspbrush_t *bspbrush, entity_t *mapent)
 +{
 +	mapbrush_t *mapbrush;
 +	side_t *side;
 +	int i, besttexinfo;
 +
 +	CheckBSPBrush(bspbrush);
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +	Error ("nummapbrushes == MAX_MAPFILE_BRUSHES");
 +
 +	mapbrush = &mapbrushes[nummapbrushes];
 +	mapbrush->original_sides = &brushsides[nummapbrushsides];
 +	mapbrush->entitynum = mapent - entities;
 +	mapbrush->brushnum = nummapbrushes - mapent->firstbrush;
 +	mapbrush->leafnum = -1;
 +	mapbrush->numsides = 0;
 +
 +	besttexinfo = TEXINFO_NODE;
 +	for (i = 0; i < bspbrush->numsides; i++)
 +	{
 +		if (!bspbrush->sides[i].winding) continue;
 +		//
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		side = &brushsides[nummapbrushsides];
 +		//the contents of the bsp brush is stored in the side variable
 +		side->contents = bspbrush->side;
 +		side->surf = 0;
 +		side->planenum = bspbrush->sides[i].planenum;
 +		side->texinfo = bspbrush->sides[i].texinfo;
 +		if (side->texinfo != TEXINFO_NODE)
 +		{
 +			//this brush side is textured
 +			side->flags |= SFL_TEXTURED;
 +			besttexinfo = side->texinfo;
 +		} //end if
 +		//
 +		nummapbrushsides++;
 +		mapbrush->numsides++;
 +	} //end for
 +	//
 +	if (besttexinfo == TEXINFO_NODE)
 +	{
 +		mapbrush->numsides = 0;
 +		q1_numclipbrushes++;
 +		return;
 +	} //end if
 +	//set the texinfo for all the brush sides without texture
 +	for (i = 0; i < mapbrush->numsides; i++)
 +	{
 +		if (mapbrush->original_sides[i].texinfo == TEXINFO_NODE)
 +		{
 +			mapbrush->original_sides[i].texinfo = besttexinfo;
 +		} //end if
 +	} //end for
 +	//contents of the brush
 +	mapbrush->contents = bspbrush->side;
 +	//
 +	if (create_aas)
 +	{
 +		//create the AAS brushes from this brush, add brush bevels
 +		AAS_CreateMapBrushes(mapbrush, mapent, true);
 +		return;
 +	} //end if
 +	//create windings for sides and bounds for brush
 +	MakeBrushWindings(mapbrush);
 +	//add brush bevels
 +	AddBrushBevels(mapbrush);
 +	//a new brush has been created
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +} //end of the function Q1_BSPBrushToMapBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_CreateMapBrushes(entity_t *mapent, int modelnum)
 +{
 +	bspbrush_t *brushlist, *brush, *nextbrush;
 +	int i;
 +
 +	//create brushes from the model BSP tree
 +	brushlist = Q1_CreateBrushesFromBSP(modelnum);
 +	//texture the brushes and split them when necesary
 +	brushlist = Q1_TextureBrushes(brushlist, modelnum);
 +	//fix the contents textures of all brushes
 +	Q1_FixContentsTextures(brushlist);
 +	//
 +	if (!nobrushmerge)
 +	{
 +		brushlist = Q1_MergeBrushes(brushlist, modelnum);
 +		//brushlist = Q1_MergeBrushes(brushlist, modelnum);
 +	} //end if
 +	//
 +	if (!modelnum) qprintf("converting brushes to map brushes\n");
 +	if (!modelnum) qprintf("%5d brushes", i = 0);
 +	for (brush = brushlist; brush; brush = nextbrush)
 +	{
 +		nextbrush = brush->next;
 +		Q1_BSPBrushToMapBrush(brush, mapent);
 +		brush->next = NULL;
 +		FreeBrush(brush);
 +		if (!modelnum) qprintf("\r%5d", ++i);
 +	} //end for
 +	if (!modelnum) qprintf("\n");
 +} //end of the function Q1_CreateMapBrushes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_ResetMapLoading(void)
 +{
 +} //end of the function Q1_ResetMapLoading
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q1_LoadMapFromBSP(char *filename, int offset, int length)
 +{
 +	int i, modelnum;
 +	char *model, *classname;
 +
 +	Log_Print("-- Q1_LoadMapFromBSP --\n");
 +	//the loaded map type
 +	loadedmaptype = MAPTYPE_QUAKE1;
 +	//
 +	qprintf("loading map from %s at %d\n", filename, offset);
 +	//load the Half-Life BSP file
 +	Q1_LoadBSPFile(filename, offset, length);
 +	//
 +	q1_numclipbrushes = 0;
 +	//CreatePath(path);
 +	//Q1_CreateQ2WALFiles(path);
 +	//parse the entities from the BSP
 +	Q1_ParseEntities();
 +	//clear the map mins and maxs
 +	ClearBounds(map_mins, map_maxs);
 +	//
 +	qprintf("creating Quake1 brushes\n");
 +	if (lessbrushes) qprintf("creating minimum number of brushes\n");
 +	else qprintf("placing textures correctly\n");
 +	//
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		entities[i].firstbrush = nummapbrushes;
 +		entities[i].numbrushes = 0;
 +		//
 +		classname = ValueForKey(&entities[i], "classname");
 +		if (classname && !strcmp(classname, "worldspawn"))
 +		{
 +			modelnum = 0;
 +		} //end if
 +		else
 +		{
 +			//
 +			model = ValueForKey(&entities[i], "model");
 +			if (!model || *model != '*') continue;
 +			model++;
 +			modelnum = atoi(model);
 +		} //end else
 +		//create map brushes for the entity
 +		Q1_CreateMapBrushes(&entities[i], modelnum);
 +	} //end for
 +	//
 +	qprintf("%5d map brushes\n", nummapbrushes);
 +	qprintf("%5d clip brushes\n", q1_numclipbrushes);
 +} //end of the function Q1_LoadMapFromBSP
 diff --git a/code/bspc/map_q2.c b/code/bspc/map_q2.c new file mode 100755 index 0000000..0f15216 --- /dev/null +++ b/code/bspc/map_q2.c @@ -0,0 +1,1162 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//===========================================================================
 +// ANSI, Area Navigational System Interface
 +// AAS,  Area Awareness System
 +//===========================================================================
 +
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"			//aas_bbox_t
 +#include "aas_store.h"		//AAS_MAX_BBOXES
 +#include "aas_cfg.h"
 +#include "aas_map.h"			//AAS_CreateMapBrushes
 +#include "l_bsp_q2.h"
 +
 +
 +#ifdef ME
 +
 +#define NODESTACKSIZE		1024
 +
 +int nodestack[NODESTACKSIZE];
 +int *nodestackptr;
 +int nodestacksize = 0;
 +int brushmodelnumbers[MAX_MAPFILE_BRUSHES];
 +int dbrushleafnums[MAX_MAPFILE_BRUSHES];
 +int dplanes2mapplanes[MAX_MAPFILE_PLANES];
 +
 +#endif //ME
 +
 +//====================================================================
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_CreateMapTexinfo(void)
 +{
 +	int i;
 +
 +	for (i = 0; i < numtexinfo; i++)
 +	{
 +		memcpy(map_texinfo[i].vecs, texinfo[i].vecs, sizeof(float) * 2 * 4);
 +		map_texinfo[i].flags = texinfo[i].flags;
 +		map_texinfo[i].value = texinfo[i].value;
 +		strcpy(map_texinfo[i].texture, texinfo[i].texture);
 +		map_texinfo[i].nexttexinfo = 0;
 +	} //end for
 +} //end of the function Q2_CreateMapTexinfo
 +
 +/*
 +===========
 +Q2_BrushContents
 +===========
 +*/
 +int	Q2_BrushContents (mapbrush_t *b)
 +{
 +	int			contents;
 +	side_t		*s;
 +	int			i;
 +	int			trans;
 +
 +	s = &b->original_sides[0];
 +	contents = s->contents;
 +	trans = texinfo[s->texinfo].flags;
 +	for (i = 1; i < b->numsides; i++, s++)
 +	{
 +		s = &b->original_sides[i];
 +		trans |= texinfo[s->texinfo].flags;
 +		if (s->contents != contents)
 +		{
 +			Log_Print("Entity %i, Brush %i: mixed face contents\n"
 +				, b->entitynum, b->brushnum);
 +			Log_Print("texture name = %s\n", texinfo[s->texinfo].texture);
 +			break;
 +		}
 +	}
 +
 +	// if any side is translucent, mark the contents
 +	// and change solid to window
 +	if ( trans & (SURF_TRANS33|SURF_TRANS66) )
 +	{
 +		contents |= CONTENTS_Q2TRANSLUCENT;
 +		if (contents & CONTENTS_SOLID)
 +		{
 +			contents &= ~CONTENTS_SOLID;
 +			contents |= CONTENTS_WINDOW;
 +		}
 +	}
 +
 +	return contents;
 +}
 +
 +#ifdef ME
 +
 +#define BBOX_NORMAL_EPSILON			0.0001
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void MakeAreaPortalBrush(mapbrush_t *brush)
 +{
 +	int sn;
 +	side_t *s;
 +
 +	brush->contents = CONTENTS_AREAPORTAL;
 +
 +	for (sn = 0; sn < brush->numsides; sn++)
 +	{
 +		s = brush->original_sides + sn;
 +		//make sure the surfaces are not hint or skip
 +		s->surf &= ~(SURF_HINT|SURF_SKIP);
 +		//
 +		s->texinfo = 0;
 +		s->contents = CONTENTS_AREAPORTAL;
 +	} //end for
 +} //end of the function MakeAreaPortalBrush
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void DPlanes2MapPlanes(void)
 +{
 +	int i;
 +
 +	for (i = 0; i < numplanes; i++)
 +	{
 +		dplanes2mapplanes[i] = FindFloatPlane(dplanes[i].normal, dplanes[i].dist);
 +	} //end for
 +} //end of the function DPlanes2MapPlanes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void MarkVisibleBrushSides(mapbrush_t *brush)
 +{
 +	int n, i, planenum;
 +	side_t *side;
 +	dface_t *face;
 +	//
 +	for (n = 0; n < brush->numsides; n++)
 +	{
 +		side = brush->original_sides + n;
 +		//if this side is a bevel or the leaf number of the brush is unknown
 +		if ((side->flags & SFL_BEVEL) || brush->leafnum < 0)
 +		{
 +			//this side is a valid splitter
 +			side->flags |= SFL_VISIBLE;
 +			continue;
 +		} //end if
 +		//assum this side will not be used as a splitter
 +		side->flags &= ~SFL_VISIBLE;
 +		//check if the side plane is used by a visible face
 +		for (i = 0; i < numfaces; i++)
 +		{
 +			face = &dfaces[i];
 +			planenum = dplanes2mapplanes[face->planenum];
 +			if ((planenum & ~1) == (side->planenum & ~1))
 +			{
 +				//this side is a valid splitter
 +				side->flags |= SFL_VISIBLE;
 +			} //end if
 +		} //end for
 +	} //end for
 +} //end of the function MarkVisibleBrushSides
 +
 +#endif //ME
 +
 +/*
 +=================
 +Q2_ParseBrush
 +=================
 +*/
 +void Q2_ParseBrush (script_t *script, entity_t *mapent)
 +{
 +	mapbrush_t *b;
 +	int i, j, k;
 +	int mt;
 +	side_t *side, *s2;
 +	int planenum;
 +	brush_texture_t td;
 +	int planepts[3][3];
 +	token_t token;
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +		Error ("nummapbrushes == MAX_MAPFILE_BRUSHES");
 +
 +	b = &mapbrushes[nummapbrushes];
 +	b->original_sides = &brushsides[nummapbrushsides];
 +	b->entitynum = num_entities-1;
 +	b->brushnum = nummapbrushes - mapent->firstbrush;
 +	b->leafnum = -1;
 +
 +	do
 +	{
 +		if (!PS_ReadToken(script, &token))
 +			break;
 +		if (!strcmp(token.string, "}") )
 +			break;
 +
 +		//IDBUG: mixed use of MAX_MAPFILE_? and MAX_MAP_? this could
 +		//			lead to out of bound indexing of the arrays
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		side = &brushsides[nummapbrushsides];
 +
 +		//read the three point plane definition
 +		for (i = 0; i < 3; i++)
 +		{
 +			if (i != 0) PS_ExpectTokenString(script, "(");
 +			for (j = 0; j < 3; j++)
 +			{
 +				PS_ExpectAnyToken(script, &token);
 +				planepts[i][j] = atof(token.string);
 +			} //end for
 +			PS_ExpectTokenString(script, ")");
 +		} //end for
 +
 +		//
 +		//read the texturedef
 +		//
 +		PS_ExpectAnyToken(script, &token);
 +		strcpy(td.name, token.string);
 +
 +		PS_ExpectAnyToken(script, &token);
 +		td.shift[0] = atol(token.string);
 +		PS_ExpectAnyToken(script, &token);
 +		td.shift[1] = atol(token.string);
 +		PS_ExpectAnyToken(script, &token);
 +		td.rotate = atol(token.string);
 +		PS_ExpectAnyToken(script, &token);
 +		td.scale[0] = atof(token.string);
 +		PS_ExpectAnyToken(script, &token);
 +		td.scale[1] = atof(token.string);
 +
 +		//find default flags and values
 +		mt = FindMiptex (td.name);
 +		td.flags = textureref[mt].flags;
 +		td.value = textureref[mt].value;
 +		side->contents = textureref[mt].contents;
 +		side->surf = td.flags = textureref[mt].flags;
 +
 +		//check if there's a number available
 +		if (PS_CheckTokenType(script, TT_NUMBER, 0, &token))
 +		{
 +			side->contents = token.intvalue;
 +			PS_ExpectTokenType(script, TT_NUMBER, 0, &token);
 +			side->surf = td.flags = token.intvalue;
 +			PS_ExpectTokenType(script, TT_NUMBER, 0, &token);
 +			td.value = token.intvalue;
 +		}
 +
 +		// translucent objects are automatically classified as detail
 +		if (side->surf & (SURF_TRANS33|SURF_TRANS66) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (fulldetail)
 +			side->contents &= ~CONTENTS_DETAIL;
 +		if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) 
 +			| CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST)  ) )
 +			side->contents |= CONTENTS_SOLID;
 +
 +		// hints and skips are never detail, and have no content
 +		if (side->surf & (SURF_HINT|SURF_SKIP) )
 +		{
 +			side->contents = 0;
 +			side->surf &= ~CONTENTS_DETAIL;
 +		}
 +
 +#ifdef ME
 +		//for creating AAS... this side is textured
 +		side->flags |= SFL_TEXTURED;
 +#endif //ME
 +		//
 +		// find the plane number
 +		//
 +		planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]);
 +		if (planenum == -1)
 +		{
 +			Log_Print("Entity %i, Brush %i: plane with no normal\n"
 +				, b->entitynum, b->brushnum);
 +			continue;
 +		}
 +
 +		//
 +		// see if the plane has been used already
 +		//
 +		for (k=0 ; k<b->numsides ; k++)
 +		{
 +			s2 = b->original_sides + k;
 +			if (s2->planenum == planenum)
 +			{
 +				Log_Print("Entity %i, Brush %i: duplicate plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +			if ( s2->planenum == (planenum^1) )
 +			{
 +				Log_Print("Entity %i, Brush %i: mirrored plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +		}
 +		if (k != b->numsides)
 +			continue;		// duplicated
 +
 +		//
 +		// keep this side
 +		//
 +
 +		side = b->original_sides + b->numsides;
 +		side->planenum = planenum;
 +		side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],
 +			&td, vec3_origin);
 +
 +		// save the td off in case there is an origin brush and we
 +		// have to recalculate the texinfo
 +		side_brushtextures[nummapbrushsides] = td;
 +
 +		nummapbrushsides++;
 +		b->numsides++;
 +	} while (1);
 +
 +	// get the content for the entire brush
 +	b->contents = Q2_BrushContents (b);
 +
 +#ifdef ME
 +	if (BrushExists(b))
 +	{
 +		c_squattbrushes++;
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	if (create_aas)
 +	{
 +		//create AAS brushes, and add brush bevels
 +		AAS_CreateMapBrushes(b, mapent, true);
 +		//NOTE: if we return here then duplicate plane errors occur for the non world entities
 +		return;
 +	} //end if
 +#endif //ME
 +
 +	// allow detail brushes to be removed 
 +	if (nodetail && (b->contents & CONTENTS_DETAIL) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	}
 +
 +	// allow water brushes to be removed
 +	if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	}
 +
 +	// create windings for sides and bounds for brush
 +	MakeBrushWindings (b);
 +
 +	// brushes that will not be visible at all will never be
 +	// used as bsp splitters
 +	if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +	{
 +		c_clipbrushes++;
 +		for (i=0 ; i<b->numsides ; i++)
 +			b->original_sides[i].texinfo = TEXINFO_NODE;
 +	}
 +
 +	//
 +	// origin brushes are removed, but they set
 +	// the rotation origin for the rest of the brushes
 +	// in the entity.  After the entire entity is parsed,
 +	// the planenums and texinfos will be adjusted for
 +	// the origin brush
 +	//
 +	if (b->contents & CONTENTS_ORIGIN)
 +	{
 +		char	string[32];
 +		vec3_t	origin;
 +
 +		if (num_entities == 1)
 +		{
 +			Error ("Entity %i, Brush %i: origin brushes not allowed in world"
 +				, b->entitynum, b->brushnum);
 +			return;
 +		}
 +
 +		VectorAdd (b->mins, b->maxs, origin);
 +		VectorScale (origin, 0.5, origin);
 +
 +		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
 +		SetKeyValue (&entities[b->entitynum], "origin", string);
 +
 +		VectorCopy (origin, entities[b->entitynum].origin);
 +
 +		// don't keep this brush
 +		b->numsides = 0;
 +
 +		return;
 +	}
 +
 +	AddBrushBevels(b);
 +
 +	nummapbrushes++;
 +	mapent->numbrushes++;		
 +}
 +
 +/*
 +================
 +Q2_MoveBrushesToWorld
 +
 +Takes all of the brushes from the current entity and
 +adds them to the world's brush list.
 +
 +Used by func_group and func_areaportal
 +================
 +*/
 +void Q2_MoveBrushesToWorld (entity_t *mapent)
 +{
 +	int			newbrushes;
 +	int			worldbrushes;
 +	mapbrush_t	*temp;
 +	int			i;
 +
 +	// this is pretty gross, because the brushes are expected to be
 +	// in linear order for each entity
 +
 +	newbrushes = mapent->numbrushes;
 +	worldbrushes = entities[0].numbrushes;
 +
 +	temp = GetMemory(newbrushes*sizeof(mapbrush_t));
 +	memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
 +
 +#if	0		// let them keep their original brush numbers
 +	for (i=0 ; i<newbrushes ; i++)
 +		temp[i].entitynum = 0;
 +#endif
 +
 +	// make space to move the brushes (overlapped copy)
 +	memmove (mapbrushes + worldbrushes + newbrushes,
 +		mapbrushes + worldbrushes,
 +		sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
 +
 +	// copy the new brushes down
 +	memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
 +
 +	// fix up indexes
 +	entities[0].numbrushes += newbrushes;
 +	for (i=1 ; i<num_entities ; i++)
 +		entities[i].firstbrush += newbrushes;
 +	FreeMemory(temp);
 +
 +	mapent->numbrushes = 0;
 +}
 +
 +/*
 +================
 +Q2_ParseMapEntity
 +================
 +*/
 +qboolean	Q2_ParseMapEntity(script_t *script)
 +{
 +	entity_t	*mapent;
 +	epair_t *e;
 +	side_t *s;
 +	int i, j;
 +	int startbrush, startsides;
 +	vec_t newdist;
 +	mapbrush_t *b;
 +	token_t token;
 +
 +	if (!PS_ReadToken(script, &token)) return false;
 +
 +	if (strcmp(token.string, "{") )
 +		Error ("ParseEntity: { not found");
 +	
 +	if (num_entities == MAX_MAP_ENTITIES)
 +		Error ("num_entities == MAX_MAP_ENTITIES");
 +
 +	startbrush = nummapbrushes;
 +	startsides = nummapbrushsides;
 +
 +	mapent = &entities[num_entities];
 +	num_entities++;
 +	memset (mapent, 0, sizeof(*mapent));
 +	mapent->firstbrush = nummapbrushes;
 +	mapent->numbrushes = 0;
 +//	mapent->portalareas[0] = -1;
 +//	mapent->portalareas[1] = -1;
 +
 +	do
 +	{
 +		if (!PS_ReadToken(script, &token))
 +		{
 +			Error("ParseEntity: EOF without closing brace");
 +		} //end if
 +		if (!strcmp(token.string, "}")) break;
 +		if (!strcmp(token.string, "{"))
 +		{
 +			Q2_ParseBrush(script, mapent);
 +		} //end if
 +		else
 +		{
 +			PS_UnreadLastToken(script);
 +			e = ParseEpair(script);
 +			e->next = mapent->epairs;
 +			mapent->epairs = e;
 +		} //end else
 +	} while(1);
 +
 +	GetVectorForKey(mapent, "origin", mapent->origin);
 +
 +	//
 +	// if there was an origin brush, offset all of the planes and texinfo
 +	//
 +	if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
 +	{
 +		for (i=0 ; i<mapent->numbrushes ; i++)
 +		{
 +			b = &mapbrushes[mapent->firstbrush + i];
 +			for (j=0 ; j<b->numsides ; j++)
 +			{
 +				s = &b->original_sides[j];
 +				newdist = mapplanes[s->planenum].dist -
 +					DotProduct (mapplanes[s->planenum].normal, mapent->origin);
 +				s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
 +				s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum],
 +					&side_brushtextures[s-brushsides], mapent->origin);
 +			}
 +			MakeBrushWindings (b);
 +		}
 +	}
 +
 +	// group entities are just for editor convenience
 +	// toss all brushes into the world entity
 +	if (!strcmp ("func_group", ValueForKey (mapent, "classname")))
 +	{
 +		Q2_MoveBrushesToWorld (mapent);
 +		mapent->numbrushes = 0;
 +		return true;
 +	}
 +
 +	// areaportal entities move their brushes, but don't eliminate
 +	// the entity
 +	if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
 +	{
 +		char	str[128];
 +
 +		if (mapent->numbrushes != 1)
 +			Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
 +
 +		b = &mapbrushes[nummapbrushes-1];
 +		b->contents = CONTENTS_AREAPORTAL;
 +		c_areaportals++;
 +		mapent->areaportalnum = c_areaportals;
 +		// set the portal number as "style"
 +		sprintf (str, "%i", c_areaportals);
 +		SetKeyValue (mapent, "style", str);
 +		Q2_MoveBrushesToWorld (mapent);
 +		return true;
 +	}
 +
 +	return true;
 +}
 +
 +//===================================================================
 +
 +/*
 +================
 +LoadMapFile
 +================
 +*/
 +void Q2_LoadMapFile(char *filename)
 +{		
 +	int i;
 +	script_t *script;
 +
 +	Log_Print("-- Q2_LoadMapFile --\n");
 +#ifdef ME
 +	//loaded map type
 +	loadedmaptype = MAPTYPE_QUAKE2;
 +	//reset the map loading
 +	ResetMapLoading();
 +#endif //ME
 +
 +	script = LoadScriptFile(filename);
 +	if (!script)
 +	{
 +		Log_Print("couldn't open %s\n", filename);
 +		return;
 +	} //end if
 +	//white spaces and escape characters inside a string are not allowed
 +	SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES |
 +									SCFL_NOSTRINGESCAPECHARS |
 +									SCFL_PRIMITIVE);
 +
 +	nummapbrushsides = 0;
 +	num_entities = 0;
 +	
 +	while (Q2_ParseMapEntity(script))
 +	{
 +	}
 +
 +	ClearBounds (map_mins, map_maxs);
 +	for (i=0 ; i<entities[0].numbrushes ; i++)
 +	{
 +		if (mapbrushes[i].mins[0] > 4096)
 +			continue;	// no valid points
 +		AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
 +		AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
 +	} //end for
 +
 +	PrintMapInfo();
 +
 +	//free the script
 +	FreeScript(script);
 +//	TestExpandBrushes ();
 +	//
 +	Q2_CreateMapTexinfo();
 +} //end of the function Q2_LoadMapFile
 +
 +#ifdef ME		//Begin MAP loading from BSP file
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_SetLeafBrushesModelNumbers(int leafnum, int modelnum)
 +{
 +	int i, brushnum;
 +	dleaf_t *leaf;
 +
 +	leaf = &dleafs[leafnum];
 +	for (i = 0; i < leaf->numleafbrushes; i++)
 +	{
 +		brushnum = dleafbrushes[leaf->firstleafbrush + i];
 +		brushmodelnumbers[brushnum] = modelnum;
 +		dbrushleafnums[brushnum] = leafnum;
 +	} //end for
 +} //end of the function Q2_SetLeafBrushesModelNumbers
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_InitNodeStack(void)
 +{
 +	nodestackptr = nodestack;
 +	nodestacksize = 0;
 +} //end of the function Q2_InitNodeStack
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_PushNodeStack(int num)
 +{
 +	*nodestackptr = num;
 +	nodestackptr++;
 +	nodestacksize++;
 +	//
 +	if (nodestackptr >= &nodestack[NODESTACKSIZE])
 +	{
 +		Error("Q2_PushNodeStack: stack overflow\n");
 +	} //end if
 +} //end of the function Q2_PushNodeStack
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Q2_PopNodeStack(void)
 +{
 +	//if the stack is empty
 +	if (nodestackptr <= nodestack) return -1;
 +	//decrease stack pointer
 +	nodestackptr--;
 +	nodestacksize--;
 +	//return the top value from the stack
 +	return *nodestackptr;
 +} //end of the function Q2_PopNodeStack
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_SetBrushModelNumbers(entity_t *mapent)
 +{
 +	int n, pn;
 +	int leafnum;
 +
 +	//
 +	Q2_InitNodeStack();
 +	//head node (root) of the bsp tree
 +	n = dmodels[mapent->modelnum].headnode;
 +	pn = 0;
 +	
 +	do
 +	{
 +		//if we are in a leaf (negative node number)
 +		if (n < 0)
 +		{
 +			//number of the leaf
 +			leafnum = (-n) - 1;
 +			//set the brush numbers
 +			Q2_SetLeafBrushesModelNumbers(leafnum, mapent->modelnum);
 +			//walk back into the tree to find a second child to continue with
 +			for (pn = Q2_PopNodeStack(); pn >= 0; n = pn, pn = Q2_PopNodeStack())
 +			{
 +				//if we took the first child at the parent node
 +				if (dnodes[pn].children[0] == n) break;
 +			} //end for
 +			//if the stack wasn't empty (if not processed whole tree)
 +			if (pn >= 0)
 +			{
 +				//push the parent node again
 +				Q2_PushNodeStack(pn);
 +				//we proceed with the second child of the parent node
 +				n = dnodes[pn].children[1];
 +			} //end if
 +		} //end if
 +		else
 +		{
 +			//push the current node onto the stack
 +			Q2_PushNodeStack(n);
 +			//walk forward into the tree to the first child
 +			n = dnodes[n].children[0];
 +		} //end else
 +	} while(pn >= 0);
 +} //end of the function Q2_SetBrushModelNumbers
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_BSPBrushToMapBrush(dbrush_t *bspbrush, entity_t *mapent)
 +{
 +	mapbrush_t *b;
 +	int i, k, n;
 +	side_t *side, *s2;
 +	int planenum;
 +	dbrushside_t *bspbrushside;
 +	dplane_t *bspplane;
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +		Error ("nummapbrushes >= MAX_MAPFILE_BRUSHES");
 +
 +	b = &mapbrushes[nummapbrushes];
 +	b->original_sides = &brushsides[nummapbrushsides];
 +	b->entitynum = mapent-entities;
 +	b->brushnum = nummapbrushes - mapent->firstbrush;
 +	b->leafnum = dbrushleafnums[bspbrush - dbrushes];
 +
 +	for (n = 0; n < bspbrush->numsides; n++)
 +	{
 +		//pointer to the bsp brush side
 +		bspbrushside = &dbrushsides[bspbrush->firstside + n];
 +
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +		{
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		} //end if
 +		//pointer to the map brush side
 +		side = &brushsides[nummapbrushsides];
 +		//if the BSP brush side is textured
 +		if (brushsidetextured[bspbrush->firstside + n]) side->flags |= SFL_TEXTURED;
 +		else side->flags &= ~SFL_TEXTURED;
 +		//ME: can get side contents and surf directly from BSP file
 +		side->contents = bspbrush->contents;
 +		//if the texinfo is TEXINFO_NODE
 +		if (bspbrushside->texinfo < 0) side->surf = 0;
 +		else side->surf = texinfo[bspbrushside->texinfo].flags;
 +
 +		// translucent objects are automatically classified as detail
 +		if (side->surf & (SURF_TRANS33|SURF_TRANS66) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (fulldetail)
 +			side->contents &= ~CONTENTS_DETAIL;
 +		if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) 
 +			| CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST)  ) )
 +			side->contents |= CONTENTS_SOLID;
 +
 +		// hints and skips are never detail, and have no content
 +		if (side->surf & (SURF_HINT|SURF_SKIP) )
 +		{
 +			side->contents = 0;
 +			side->surf &= ~CONTENTS_DETAIL;
 +		}
 +
 +		//ME: get a plane for this side
 +		bspplane = &dplanes[bspbrushside->planenum];
 +		planenum = FindFloatPlane(bspplane->normal, bspplane->dist);
 +		//
 +		// see if the plane has been used already
 +		//
 +		//ME: this really shouldn't happen!!!
 +		//ME: otherwise the bsp file is corrupted??
 +		//ME: still it seems to happen, maybe Johny Boy's
 +		//ME: brush bevel adding is crappy ?
 +		for (k = 0; k < b->numsides; k++)
 +		{
 +			s2 = b->original_sides + k;
 +//			if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999
 +//							&& fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 )
 +
 +			if (s2->planenum == planenum)
 +			{
 +				Log_Print("Entity %i, Brush %i: duplicate plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +			if ( s2->planenum == (planenum^1) )
 +			{
 +				Log_Print("Entity %i, Brush %i: mirrored plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +		}
 +		if (k != b->numsides)
 +			continue;		// duplicated
 +
 +		//
 +		// keep this side
 +		//
 +		//ME: reset pointer to side, why? hell I dunno (pointer is set above already)
 +		side = b->original_sides + b->numsides;
 +		//ME: store the plane number
 +		side->planenum = planenum;
 +		//ME: texinfo is already stored when bsp is loaded
 +		//NOTE: check for TEXINFO_NODE, otherwise crash in Q2_BrushContents
 +		if (bspbrushside->texinfo < 0) side->texinfo = 0;
 +		else side->texinfo = bspbrushside->texinfo;
 +
 +		// save the td off in case there is an origin brush and we
 +		// have to recalculate the texinfo
 +		// ME: don't need to recalculate because it's already done
 +		//     (for non-world entities) in the BSP file
 +//		side_brushtextures[nummapbrushsides] = td;
 +
 +		nummapbrushsides++;
 +		b->numsides++;
 +	} //end for
 +
 +	// get the content for the entire brush
 +	b->contents = bspbrush->contents;
 +	Q2_BrushContents(b);
 +
 +	if (BrushExists(b))
 +	{
 +		c_squattbrushes++;
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	//if we're creating AAS
 +	if (create_aas)
 +	{
 +		//create the AAS brushes from this brush, don't add brush bevels
 +		AAS_CreateMapBrushes(b, mapent, false);
 +		return;
 +	} //end if
 +
 +	// allow detail brushes to be removed 
 +	if (nodetail && (b->contents & CONTENTS_DETAIL) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	// allow water brushes to be removed
 +	if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	// create windings for sides and bounds for brush
 +	MakeBrushWindings(b);
 +
 +	//mark brushes without winding or with a tiny window as bevels
 +	MarkBrushBevels(b);
 +
 +	// brushes that will not be visible at all will never be
 +	// used as bsp splitters
 +	if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +	{
 +			c_clipbrushes++;
 +		for (i = 0; i < b->numsides; i++)
 +			b->original_sides[i].texinfo = TEXINFO_NODE;
 +	} //end for
 +
 +	//
 +	// origin brushes are removed, but they set
 +	// the rotation origin for the rest of the brushes
 +	// in the entity.  After the entire entity is parsed,
 +	// the planenums and texinfos will be adjusted for
 +	// the origin brush
 +	//
 +	//ME: not needed because the entities in the BSP file already
 +	//    have an origin set
 +//	if (b->contents & CONTENTS_ORIGIN)
 +//	{
 +//		char	string[32];
 +//		vec3_t	origin;
 +//
 +//		if (num_entities == 1)
 +//		{
 +//			Error ("Entity %i, Brush %i: origin brushes not allowed in world"
 +//				, b->entitynum, b->brushnum);
 +//			return;
 +//		}
 +//
 +//		VectorAdd (b->mins, b->maxs, origin);
 +//		VectorScale (origin, 0.5, origin);
 +//
 +//		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
 +//		SetKeyValue (&entities[b->entitynum], "origin", string);
 +//
 +//		VectorCopy (origin, entities[b->entitynum].origin);
 +//
 +//		// don't keep this brush
 +//		b->numsides = 0;
 +//
 +//		return;
 +//	}
 +
 +	//ME: the bsp brushes already have bevels, so we won't try to
 +	//    add them again (especially since Johny Boy's bevel adding might
 +	//    be crappy)
 +//	AddBrushBevels(b);
 +
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +} //end of the function Q2_BSPBrushToMapBrush
 +//===========================================================================
 +//===========================================================================
 +void Q2_ParseBSPBrushes(entity_t *mapent)
 +{
 +	int i;
 +
 +	//give all the brushes that belong to this entity the number of the
 +	//BSP model used by this entity
 +	Q2_SetBrushModelNumbers(mapent);
 +	//now parse all the brushes with the correct mapent->modelnum
 +	for (i = 0; i < numbrushes; i++)
 +	{
 +		if (brushmodelnumbers[i] == mapent->modelnum)
 +		{
 +			Q2_BSPBrushToMapBrush(&dbrushes[i], mapent);
 +		} //end if
 +	} //end for
 +} //end of the function Q2_ParseBSPBrushes
 +//===========================================================================
 +//===========================================================================
 +qboolean Q2_ParseBSPEntity(int entnum)
 +{
 +	entity_t	*mapent;
 +	char *model;
 +	int startbrush, startsides;
 +
 +	startbrush = nummapbrushes;
 +	startsides = nummapbrushsides;
 +
 +	mapent = &entities[entnum];//num_entities];
 +	mapent->firstbrush = nummapbrushes;
 +	mapent->numbrushes = 0;
 +	mapent->modelnum = -1;	//-1 = no model
 +
 +	model = ValueForKey(mapent, "model");
 +	if (model && strlen(model))
 +	{
 +		if (*model != '*')
 +		{
 +			Error("Q2_ParseBSPEntity: model number without leading *");
 +		} //end if
 +		//get the model number of this entity (skip the leading *)
 +		mapent->modelnum = atoi(&model[1]);
 +	} //end if
 +
 +	GetVectorForKey(mapent, "origin", mapent->origin);
 +
 +	//if this is the world entity it has model number zero
 +	//the world entity has no model key
 +	if (!strcmp("worldspawn", ValueForKey(mapent, "classname")))
 +	{
 +		mapent->modelnum = 0;
 +	} //end if
 +	//if the map entity has a BSP model (a modelnum of -1 is used for
 +	//entities that aren't using a BSP model)
 +	if (mapent->modelnum >= 0)
 +	{
 +		//parse the bsp brushes
 +		Q2_ParseBSPBrushes(mapent);
 +	} //end if
 +	//
 +	//the origin of the entity is already taken into account
 +	//
 +	//func_group entities can't be in the bsp file
 +	//
 +	//check out the func_areaportal entities
 +	if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
 +	{
 +		c_areaportals++;
 +		mapent->areaportalnum = c_areaportals;
 +		return true;
 +	} //end if
 +	return true;
 +} //end of the function Q2_ParseBSPEntity
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q2_LoadMapFromBSP(char *filename, int offset, int length)
 +{
 +	int i;
 +
 +	Log_Print("-- Q2_LoadMapFromBSP --\n");
 +	//loaded map type
 +	loadedmaptype = MAPTYPE_QUAKE2;
 +
 +	Log_Print("Loading map from %s...\n", filename);
 +	//load the bsp file
 +	Q2_LoadBSPFile(filename, offset, length);
 +
 +	//create an index from bsp planes to map planes
 +	//DPlanes2MapPlanes();
 +	//clear brush model numbers
 +	for (i = 0; i < MAX_MAPFILE_BRUSHES; i++)
 +		brushmodelnumbers[i] = -1;
 +
 +	nummapbrushsides = 0;
 +	num_entities = 0;
 +
 +	Q2_ParseEntities();
 +	//
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		Q2_ParseBSPEntity(i);
 +	} //end for
 +
 +	//get the map mins and maxs from the world model
 +	ClearBounds(map_mins, map_maxs);
 +	for (i = 0; i < entities[0].numbrushes; i++)
 +	{
 +		if (mapbrushes[i].mins[0] > 4096)
 +			continue;	//no valid points
 +		AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
 +		AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
 +	} //end for
 +
 +	PrintMapInfo();
 +	//
 +	Q2_CreateMapTexinfo();
 +} //end of the function Q2_LoadMapFromBSP
 +
 +void Q2_ResetMapLoading(void)
 +{
 +	//reset for map loading from bsp
 +	memset(nodestack, 0, NODESTACKSIZE * sizeof(int));
 +	nodestackptr = NULL;
 +	nodestacksize = 0;
 +	memset(brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof(int));
 +} //end of the function Q2_ResetMapLoading
 +
 +//End MAP loading from BSP file
 +#endif //ME
 +
 +//====================================================================
 +
 +/*
 +================
 +TestExpandBrushes
 +
 +Expands all the brush planes and saves a new map out
 +================
 +*/
 +void TestExpandBrushes (void)
 +{
 +	FILE	*f;
 +	side_t	*s;
 +	int		i, j, bn;
 +	winding_t	*w;
 +	char	*name = "expanded.map";
 +	mapbrush_t	*brush;
 +	vec_t	dist;
 +
 +	Log_Print("writing %s\n", name);
 +	f = fopen (name, "wb");
 +	if (!f)
 +		Error ("Can't write %s\n", name);
 +
 +	fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
 +
 +	for (bn=0 ; bn<nummapbrushes ; bn++)
 +	{
 +		brush = &mapbrushes[bn];
 +		fprintf (f, "{\n");
 +		for (i=0 ; i<brush->numsides ; i++)
 +		{
 +			s = brush->original_sides + i;
 +			dist = mapplanes[s->planenum].dist;
 +			for (j=0 ; j<3 ; j++)
 +				dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
 +
 +			w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);
 +
 +			fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
 +			fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
 +			fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
 +
 +			fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
 +			FreeWinding (w);
 +		}
 +		fprintf (f, "}\n");
 +	}
 +	fprintf (f, "}\n");
 +
 +	fclose (f);
 +
 +	Error ("can't proceed after expanding brushes");
 +} //end of the function TestExpandBrushes
 +
 diff --git a/code/bspc/map_q3.c b/code/bspc/map_q3.c new file mode 100755 index 0000000..750b304 --- /dev/null +++ b/code/bspc/map_q3.c @@ -0,0 +1,681 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"	//aas_bbox_t
 +#include "aas_store.h"			//AAS_MAX_BBOXES
 +#include "aas_cfg.h"
 +#include "aas_map.h"			//AAS_CreateMapBrushes
 +#include "l_bsp_q3.h"
 +#include "../qcommon/cm_patch.h"
 +#include "../game/surfaceflags.h"
 +
 +#define NODESTACKSIZE		1024
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintContents(int contents);
 +
 +int	Q3_BrushContents(mapbrush_t *b)
 +{
 +	int contents, i, mixed, hint;
 +	side_t *s;
 +
 +	s = &b->original_sides[0];
 +	contents = s->contents;
 +	//
 +	mixed = false;
 +	hint = false;
 +	for (i = 1; i < b->numsides; i++)
 +	{
 +		s = &b->original_sides[i];
 +		if (s->contents != contents) mixed = true;
 +		if (s->surf & (SURF_HINT|SURF_SKIP)) hint = true;
 +		contents |= s->contents;
 +	} //end for
 +	//
 +	if (hint)
 +	{
 +		if (contents)
 +		{
 +			Log_Write("WARNING: hint brush with contents: ");
 +			PrintContents(contents);
 +			Log_Write("\r\n");
 +			//
 +			Log_Write("brush contents is: ");
 +			PrintContents(b->contents);
 +			Log_Write("\r\n");
 +		} //end if
 +		return 0;
 +	} //end if
 +	//Log_Write("brush %d contents ", nummapbrushes);
 +	//PrintContents(contents);
 +	//Log_Write("\r\n");
 +	//remove ladder and fog contents
 +	contents &= ~(CONTENTS_LADDER|CONTENTS_FOG);
 +	//
 +	if (mixed)
 +	{
 +		Log_Write("Entity %i, Brush %i: mixed face contents "
 +			, b->entitynum, b->brushnum);
 +		PrintContents(contents);
 +		Log_Write("\r\n");
 +		//
 +		Log_Write("brush contents is: ");
 +		PrintContents(b->contents);
 +		Log_Write("\r\n");
 +		//
 +		if (contents & CONTENTS_DONOTENTER) return CONTENTS_DONOTENTER;//Log_Print("mixed contents with donotenter\n");
 +		/*
 +		Log_Print("contents:"); PrintContents(contents);
 +		Log_Print("\ncontents:"); PrintContents(s->contents);
 +		Log_Print("\n");
 +		Log_Print("texture name = %s\n", texinfo[s->texinfo].texture);
 +		*/
 +		//if liquid brush
 +		if (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER))
 +		{
 +			return (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER));
 +		} //end if
 +		if (contents & CONTENTS_PLAYERCLIP) return (contents & CONTENTS_PLAYERCLIP);
 +		return (contents & CONTENTS_SOLID);
 +	} //end if
 +	/*
 +	if (contents & CONTENTS_AREAPORTAL)
 +	{
 +		static int num;
 +		Log_Write("Entity %i, Brush %i: area portal %d\r\n", b->entitynum, b->brushnum, num++);
 +	} //end if*/
 +	if (contents == (contents & CONTENTS_STRUCTURAL))
 +	{
 +		//Log_Print("brush %i is only structural\n", b->brushnum);
 +		contents = 0;
 +	} //end if
 +	if (contents & CONTENTS_DONOTENTER)
 +	{
 +		Log_Print("brush %i is a donotenter brush, c = %X\n", b->brushnum, contents);
 +	} //end if
 +	return contents;
 +} //end of the function Q3_BrushContents
 +#define BBOX_NORMAL_EPSILON			0.0001
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_DPlanes2MapPlanes(void)
 +{
 +	int i;
 +
 +	for (i = 0; i < q3_numplanes; i++)
 +	{
 +		dplanes2mapplanes[i] = FindFloatPlane(q3_dplanes[i].normal, q3_dplanes[i].dist);
 +	} //end for
 +} //end of the function Q3_DPlanes2MapPlanes
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_BSPBrushToMapBrush(q3_dbrush_t *bspbrush, entity_t *mapent)
 +{
 +	mapbrush_t *b;
 +	int i, k, n;
 +	side_t *side, *s2;
 +	int planenum;
 +	q3_dbrushside_t *bspbrushside;
 +	q3_dplane_t *bspplane;
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +		Error ("nummapbrushes >= MAX_MAPFILE_BRUSHES");
 +
 +	b = &mapbrushes[nummapbrushes];
 +	b->original_sides = &brushsides[nummapbrushsides];
 +	b->entitynum = mapent-entities;
 +	b->brushnum = nummapbrushes - mapent->firstbrush;
 +	b->leafnum = dbrushleafnums[bspbrush - q3_dbrushes];
 +
 +	for (n = 0; n < bspbrush->numSides; n++)
 +	{
 +		//pointer to the bsp brush side
 +		bspbrushside = &q3_dbrushsides[bspbrush->firstSide + n];
 +
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +		{
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		} //end if
 +		//pointer to the map brush side
 +		side = &brushsides[nummapbrushsides];
 +		//if the BSP brush side is textured
 +		if (q3_dbrushsidetextured[bspbrush->firstSide + n]) side->flags |= SFL_TEXTURED|SFL_VISIBLE;
 +		else side->flags &= ~SFL_TEXTURED;
 +		//NOTE: all Quake3 sides are assumed textured
 +		//side->flags |= SFL_TEXTURED|SFL_VISIBLE;
 +		//
 +		if (bspbrushside->shaderNum < 0)
 +		{
 +			side->contents = 0;
 +			side->surf = 0;
 +		} //end if
 +		else
 +		{
 +			side->contents = q3_dshaders[bspbrushside->shaderNum].contentFlags;
 +			side->surf = q3_dshaders[bspbrushside->shaderNum].surfaceFlags;
 +			if (strstr(q3_dshaders[bspbrushside->shaderNum].shader, "common/hint"))
 +			{
 +				//Log_Print("found hint side\n");
 +				side->surf |= SURF_HINT;
 +			} //end if
 +		} //end else
 +		//
 +		if (side->surf & SURF_NODRAW)
 +		{
 +			side->flags |= SFL_TEXTURED|SFL_VISIBLE;
 +		} //end if
 +		/*
 +		if (side->contents & (CONTENTS_TRANSLUCENT|CONTENTS_STRUCTURAL))
 +		{
 +			side->flags |= SFL_TEXTURED|SFL_VISIBLE;
 +		} //end if*/
 +
 +		// hints and skips are never detail, and have no content
 +		if (side->surf & (SURF_HINT|SURF_SKIP) )
 +		{
 +			side->contents = 0;
 +			//Log_Print("found hint brush side\n");
 +		}
 +		/*
 +		if ((side->surf & SURF_NODRAW) && (side->surf & SURF_NOIMPACT))
 +		{
 +			side->contents = 0;
 +			side->surf &= ~CONTENTS_DETAIL;
 +			Log_Print("probably found hint brush in a BSP without hints being used\n");
 +		} //end if*/
 +
 +		//ME: get a plane for this side
 +		bspplane = &q3_dplanes[bspbrushside->planeNum];
 +		planenum = FindFloatPlane(bspplane->normal, bspplane->dist);
 +		//
 +		// see if the plane has been used already
 +		//
 +		//ME: this really shouldn't happen!!!
 +		//ME: otherwise the bsp file is corrupted??
 +		//ME: still it seems to happen, maybe Johny Boy's
 +		//ME: brush bevel adding is crappy ?
 +		for (k = 0; k < b->numsides; k++)
 +		{
 +			s2 = b->original_sides + k;
 +//			if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999
 +//							&& fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 )
 +
 +			if (s2->planenum == planenum)
 +			{
 +				Log_Print("Entity %i, Brush %i: duplicate plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +			if ( s2->planenum == (planenum^1) )
 +			{
 +				Log_Print("Entity %i, Brush %i: mirrored plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +		}
 +		if (k != b->numsides)
 +			continue;		// duplicated
 +
 +		//
 +		// keep this side
 +		//
 +		//ME: reset pointer to side, why? hell I dunno (pointer is set above already)
 +		side = b->original_sides + b->numsides;
 +		//ME: store the plane number
 +		side->planenum = planenum;
 +		//ME: texinfo is already stored when bsp is loaded
 +		//NOTE: check for TEXINFO_NODE, otherwise crash in Q3_BrushContents
 +		//if (bspbrushside->texinfo < 0) side->texinfo = 0;
 +		//else side->texinfo = bspbrushside->texinfo;
 +
 +		// save the td off in case there is an origin brush and we
 +		// have to recalculate the texinfo
 +		// ME: don't need to recalculate because it's already done
 +		//     (for non-world entities) in the BSP file
 +//		side_brushtextures[nummapbrushsides] = td;
 +
 +		nummapbrushsides++;
 +		b->numsides++;
 +	} //end for
 +
 +	// get the content for the entire brush
 +	b->contents = q3_dshaders[bspbrush->shaderNum].contentFlags;
 +	b->contents &= ~(CONTENTS_LADDER|CONTENTS_FOG|CONTENTS_STRUCTURAL);
 +//	b->contents = Q3_BrushContents(b);
 +	//
 +
 +	if (BrushExists(b))
 +	{
 +		c_squattbrushes++;
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	//if we're creating AAS
 +	if (create_aas)
 +	{
 +		//create the AAS brushes from this brush, don't add brush bevels
 +		AAS_CreateMapBrushes(b, mapent, false);
 +		return;
 +	} //end if
 +
 +	// allow detail brushes to be removed 
 +	if (nodetail && (b->contents & CONTENTS_DETAIL) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	// allow water brushes to be removed
 +	if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	// create windings for sides and bounds for brush
 +	MakeBrushWindings(b);
 +
 +	//mark brushes without winding or with a tiny window as bevels
 +	MarkBrushBevels(b);
 +
 +	// brushes that will not be visible at all will never be
 +	// used as bsp splitters
 +	if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +	{
 +			c_clipbrushes++;
 +		for (i = 0; i < b->numsides; i++)
 +			b->original_sides[i].texinfo = TEXINFO_NODE;
 +	} //end for
 +
 +	//
 +	// origin brushes are removed, but they set
 +	// the rotation origin for the rest of the brushes
 +	// in the entity.  After the entire entity is parsed,
 +	// the planenums and texinfos will be adjusted for
 +	// the origin brush
 +	//
 +	//ME: not needed because the entities in the BSP file already
 +	//    have an origin set
 +//	if (b->contents & CONTENTS_ORIGIN)
 +//	{
 +//		char	string[32];
 +//		vec3_t	origin;
 +//
 +//		if (num_entities == 1)
 +//		{
 +//			Error ("Entity %i, Brush %i: origin brushes not allowed in world"
 +//				, b->entitynum, b->brushnum);
 +//			return;
 +//		}
 +//
 +//		VectorAdd (b->mins, b->maxs, origin);
 +//		VectorScale (origin, 0.5, origin);
 +//
 +//		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
 +//		SetKeyValue (&entities[b->entitynum], "origin", string);
 +//
 +//		VectorCopy (origin, entities[b->entitynum].origin);
 +//
 +//		// don't keep this brush
 +//		b->numsides = 0;
 +//
 +//		return;
 +//	}
 +
 +	//ME: the bsp brushes already have bevels, so we won't try to
 +	//    add them again (especially since Johny Boy's bevel adding might
 +	//    be crappy)
 +//	AddBrushBevels(b);
 +
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +} //end of the function Q3_BSPBrushToMapBrush
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_ParseBSPBrushes(entity_t *mapent)
 +{
 +	int i;
 +
 +	for (i = 0; i < q3_dmodels[mapent->modelnum].numBrushes; i++)
 +	{
 +		Q3_BSPBrushToMapBrush(&q3_dbrushes[q3_dmodels[mapent->modelnum].firstBrush + i], mapent);
 +	} //end for
 +} //end of the function Q3_ParseBSPBrushes
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean Q3_ParseBSPEntity(int entnum)
 +{
 +	entity_t *mapent;
 +	char *model;
 +	int startbrush, startsides;
 +
 +	startbrush = nummapbrushes;
 +	startsides = nummapbrushsides;
 +
 +	mapent = &entities[entnum];//num_entities];
 +	mapent->firstbrush = nummapbrushes;
 +	mapent->numbrushes = 0;
 +	mapent->modelnum = -1;	//-1 = no BSP model
 +
 +	model = ValueForKey(mapent, "model");
 +	if (model && strlen(model))
 +	{
 +		if (*model == '*')
 +		{
 +			//get the model number of this entity (skip the leading *)
 +			mapent->modelnum = atoi(&model[1]);
 +		} //end if
 +	} //end if
 +
 +	GetVectorForKey(mapent, "origin", mapent->origin);
 +
 +	//if this is the world entity it has model number zero
 +	//the world entity has no model key
 +	if (!strcmp("worldspawn", ValueForKey(mapent, "classname")))
 +	{
 +		mapent->modelnum = 0;
 +	} //end if
 +	//if the map entity has a BSP model (a modelnum of -1 is used for
 +	//entities that aren't using a BSP model)
 +	if (mapent->modelnum >= 0)
 +	{
 +		//parse the bsp brushes
 +		Q3_ParseBSPBrushes(mapent);
 +	} //end if
 +	//
 +	//the origin of the entity is already taken into account
 +	//
 +	//func_group entities can't be in the bsp file
 +	//
 +	//check out the func_areaportal entities
 +	if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
 +	{
 +		c_areaportals++;
 +		mapent->areaportalnum = c_areaportals;
 +		return true;
 +	} //end if
 +	return true;
 +} //end of the function Q3_ParseBSPEntity
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +#define	MAX_PATCH_VERTS		1024
 +
 +void AAS_CreateCurveBrushes(void)
 +{
 +	int i, j, n, planenum, numcurvebrushes = 0;
 +	q3_dsurface_t *surface;
 +	q3_drawVert_t *dv_p;
 +	vec3_t points[MAX_PATCH_VERTS];
 +	int width, height, c;
 +	patchCollide_t *pc;
 +	facet_t *facet;
 +	mapbrush_t *brush;
 +	side_t *side;
 +	entity_t *mapent;
 +	winding_t *winding;
 +
 +	qprintf("nummapbrushsides = %d\n", nummapbrushsides);
 +	mapent = &entities[0];
 +	for (i = 0; i < q3_numDrawSurfaces; i++)
 +	{
 +		surface = &q3_drawSurfaces[i];
 +		if ( ! surface->patchWidth ) continue;
 +		// if the curve is not solid
 +		if (!(q3_dshaders[surface->shaderNum].contentFlags & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP)))
 +		{
 +			//Log_Print("skipped non-solid curve\n");
 +			continue;
 +		} //end if
 +		// if this curve should not be used for AAS
 +		if ( q3_dshaders[surface->shaderNum].contentFlags & CONTENTS_NOBOTCLIP ) {
 +			continue;
 +		}
 +		//
 +		width = surface->patchWidth;
 +		height = surface->patchHeight;
 +		c = width * height;
 +		if (c > MAX_PATCH_VERTS)
 +		{
 +			Error("ParseMesh: MAX_PATCH_VERTS");
 +		} //end if
 +
 +		dv_p = q3_drawVerts + surface->firstVert;
 +		for ( j = 0 ; j < c ; j++, dv_p++ )
 +		{
 +			points[j][0] = dv_p->xyz[0];
 +			points[j][1] = dv_p->xyz[1];
 +			points[j][2] = dv_p->xyz[2];
 +		} //end for
 +		// create the internal facet structure
 +		pc = CM_GeneratePatchCollide(width, height, points);
 +		//
 +		for (j = 0; j < pc->numFacets; j++)
 +		{
 +			facet = &pc->facets[j];
 +			//
 +			brush = &mapbrushes[nummapbrushes];
 +			brush->original_sides = &brushsides[nummapbrushsides];
 +			brush->entitynum = 0;
 +			brush->brushnum = nummapbrushes - mapent->firstbrush;
 +			//
 +			brush->numsides = facet->numBorders + 2;
 +			nummapbrushsides += brush->numsides;
 +			brush->contents = CONTENTS_SOLID;
 +			//
 +			//qprintf("\r%6d curve brushes", nummapbrushsides);//++numcurvebrushes);
 +			qprintf("\r%6d curve brushes", ++numcurvebrushes);
 +			//
 +			planenum = FindFloatPlane(pc->planes[facet->surfacePlane].plane, pc->planes[facet->surfacePlane].plane[3]);
 +			//
 +			side = &brush->original_sides[0];
 +			side->planenum = planenum;
 +			side->contents = CONTENTS_SOLID;
 +			side->flags |= SFL_TEXTURED|SFL_VISIBLE|SFL_CURVE;
 +			side->surf = 0;
 +			//
 +			side = &brush->original_sides[1];
 +			if (create_aas)
 +			{
 +				//the plane is expanded later so it's not a problem that
 +				//these first two opposite sides are coplanar
 +				side->planenum = planenum ^ 1;
 +			} //end if
 +			else
 +			{
 +				side->planenum = FindFloatPlane(mapplanes[planenum^1].normal, mapplanes[planenum^1].dist + 1);
 +				side->flags |= SFL_TEXTURED|SFL_VISIBLE;
 +			} //end else
 +			side->contents = CONTENTS_SOLID;
 +			side->flags |= SFL_CURVE;
 +			side->surf = 0;
 +			//
 +			winding = BaseWindingForPlane(mapplanes[side->planenum].normal, mapplanes[side->planenum].dist);
 +			for (n = 0; n < facet->numBorders; n++)
 +			{
 +				//never use the surface plane as a border
 +				if (facet->borderPlanes[n] == facet->surfacePlane) continue;
 +				//
 +				side = &brush->original_sides[2 + n];
 +				side->planenum = FindFloatPlane(pc->planes[facet->borderPlanes[n]].plane, pc->planes[facet->borderPlanes[n]].plane[3]);
 +				if (facet->borderInward[n]) side->planenum ^= 1;
 +				side->contents = CONTENTS_SOLID;
 +				side->flags |= SFL_TEXTURED|SFL_CURVE;
 +				side->surf = 0;
 +				//chop the winding in place
 +				if (winding) ChopWindingInPlace(&winding, mapplanes[side->planenum^1].normal, mapplanes[side->planenum^1].dist, 0.1); //CLIP_EPSILON);
 +			} //end for
 +			//VectorCopy(pc->bounds[0], brush->mins);
 +			//VectorCopy(pc->bounds[1], brush->maxs);
 +			if (!winding)
 +			{
 +				Log_Print("WARNING: AAS_CreateCurveBrushes: no winding\n");
 +				brush->numsides = 0;
 +				continue;
 +			} //end if
 +			brush->original_sides[0].winding = winding;
 +			WindingBounds(winding, brush->mins, brush->maxs);
 +			for (n = 0; n < 3; n++)
 +			{
 +				//IDBUG: all the indexes into the mins and maxs were zero (not using i)
 +				if (brush->mins[n] < -MAX_MAP_BOUNDS || brush->maxs[n] > MAX_MAP_BOUNDS)
 +				{
 +					Log_Print("entity %i, brush %i: bounds out of range\n", brush->entitynum, brush->brushnum);
 +					Log_Print("brush->mins[%d] = %f, brush->maxs[%d] = %f\n", n, brush->mins[n], n, brush->maxs[n]);
 +					brush->numsides = 0; //remove the brush
 +					break;
 +				} //end if
 +				if (brush->mins[n] > MAX_MAP_BOUNDS || brush->maxs[n] < -MAX_MAP_BOUNDS)
 +				{
 +					Log_Print("entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum);
 +					Log_Print("brush->mins[%d] = %f, brush->maxs[%d] = %f\n", n, brush->mins[n], n, brush->maxs[n]);
 +					brush->numsides = 0; //remove the brush
 +					break;
 +				} //end if
 +			} //end for
 +			if (create_aas)
 +			{
 +				//NOTE: brush bevels now already added
 +				//AddBrushBevels(brush);
 +				AAS_CreateMapBrushes(brush, mapent, false);
 +			} //end if
 +			else
 +			{
 +				// create windings for sides and bounds for brush
 +				MakeBrushWindings(brush);
 +				AddBrushBevels(brush);
 +				nummapbrushes++;
 +				mapent->numbrushes++;
 +			} //end else
 +		} //end for
 +	} //end for
 +	//qprintf("\r%6d curve brushes", nummapbrushsides);//++numcurvebrushes);
 +	qprintf("\r%6d curve brushes\n", numcurvebrushes);
 +} //end of the function AAS_CreateCurveBrushes
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void AAS_ExpandMapBrush(mapbrush_t *brush, vec3_t mins, vec3_t maxs);
 +
 +void Q3_LoadMapFromBSP(struct quakefile_s *qf)
 +{
 +	int i;
 +	vec3_t mins = {-1,-1,-1}, maxs = {1, 1, 1};
 +
 +	Log_Print("-- Q3_LoadMapFromBSP --\n");
 +	//loaded map type
 +	loadedmaptype = MAPTYPE_QUAKE3;
 +
 +	Log_Print("Loading map from %s...\n", qf->filename);
 +	//load the bsp file
 +	Q3_LoadBSPFile(qf);
 +
 +	//create an index from bsp planes to map planes
 +	//DPlanes2MapPlanes();
 +	//clear brush model numbers
 +	for (i = 0; i < MAX_MAPFILE_BRUSHES; i++)
 +		brushmodelnumbers[i] = -1;
 +
 +	nummapbrushsides = 0;
 +	num_entities = 0;
 +
 +	Q3_ParseEntities();
 +	//
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		Q3_ParseBSPEntity(i);
 +	} //end for
 +
 +	AAS_CreateCurveBrushes();
 +	//get the map mins and maxs from the world model
 +	ClearBounds(map_mins, map_maxs);
 +	for (i = 0; i < entities[0].numbrushes; i++)
 +	{
 +		if (mapbrushes[i].numsides <= 0)
 +			continue;
 +		AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
 +		AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
 +	} //end for
 +	/*/
 +	for (i = 0; i < nummapbrushes; i++)
 +	{
 +		//if (!mapbrushes[i].original_sides) continue;
 +		//AddBrushBevels(&mapbrushes[i]);
 +		//AAS_ExpandMapBrush(&mapbrushes[i], mins, maxs);
 +	} //end for*/
 +	/*
 +	for (i = 0; i < nummapbrushsides; i++)
 +	{
 +		Log_Write("side %d flags = %d", i, brushsides[i].flags);
 +	} //end for
 +	for (i = 0; i < nummapbrushes; i++)
 +	{
 +		Log_Write("brush contents: ");
 +		PrintContents(mapbrushes[i].contents);
 +		Log_Print("\n");
 +	} //end for*/
 +} //end of the function Q3_LoadMapFromBSP
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Q3_ResetMapLoading(void)
 +{
 +	//reset for map loading from bsp
 +	memset(nodestack, 0, NODESTACKSIZE * sizeof(int));
 +	nodestackptr = NULL;
 +	nodestacksize = 0;
 +	memset(brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof(int));
 +} //end of the function Q3_ResetMapLoading
 +
 diff --git a/code/bspc/map_sin.c b/code/bspc/map_sin.c new file mode 100755 index 0000000..7794b8c --- /dev/null +++ b/code/bspc/map_sin.c @@ -0,0 +1,1211 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +//-----------------------------------------------------------------------------
 +//
 +//  $Logfile:: /MissionPack/code/bspc/map_sin.c                               $
 +
 +#include "qbsp.h"
 +#include "l_bsp_sin.h"
 +#include "aas_map.h"			//AAS_CreateMapBrushes
 +
 +
 +//====================================================================
 +
 +
 +/*
 +===========
 +Sin_BrushContents
 +===========
 +*/
 +
 +int Sin_BrushContents(mapbrush_t *b)
 +{
 +	int			contents;
 +	side_t		*s;
 +	int			i;
 +#ifdef SIN
 +	float			trans = 0;
 +#else
 +	int			trans;
 +#endif
 +
 +	s = &b->original_sides[0];
 +	contents = s->contents;
 +
 +#ifdef SIN
 +	trans = sin_texinfo[s->texinfo].translucence;
 +#else
 +	trans = texinfo[s->texinfo].flags;
 +#endif
 +	for (i=1 ; i<b->numsides ; i++, s++)
 +	{
 +		s = &b->original_sides[i];
 +#ifdef SIN
 +		trans += sin_texinfo[s->texinfo].translucence;
 +#else
 +		trans |= texinfo[s->texinfo].flags;
 +#endif
 +		if (s->contents != contents)
 +		{
 +#ifdef SIN
 +      if ( 
 +            ( s->contents & CONTENTS_DETAIL && !(contents & CONTENTS_DETAIL) ) ||
 +            ( !(s->contents & CONTENTS_DETAIL) && contents & CONTENTS_DETAIL ) 
 +         )
 +         {
 +         s->contents |= CONTENTS_DETAIL;
 +         contents |= CONTENTS_DETAIL;
 +         continue;
 +         }
 +#endif
 +			printf ("Entity %i, Brush %i: mixed face contents\n"
 +				, b->entitynum, b->brushnum);
 +			break;
 +		}
 +	}
 +
 +
 +#ifdef SIN
 +	if (contents & CONTENTS_FENCE)
 +	{
 +//		contents |= CONTENTS_TRANSLUCENT;
 +		contents |= CONTENTS_DETAIL;
 +		contents |= CONTENTS_DUMMYFENCE;
 +		contents &= ~CONTENTS_SOLID;
 +		contents &= ~CONTENTS_FENCE;
 +		contents |= CONTENTS_WINDOW;
 +	}
 +#endif
 +
 +	// if any side is translucent, mark the contents
 +	// and change solid to window
 +#ifdef SIN
 +	if ( trans > 0 )
 +#else
 +	if ( trans & (SURF_TRANS33|SURF_TRANS66) )
 +#endif
 +	{
 +		contents |= CONTENTS_Q2TRANSLUCENT;
 +		if (contents & CONTENTS_SOLID)
 +		{
 +			contents &= ~CONTENTS_SOLID;
 +			contents |= CONTENTS_WINDOW;
 +		}
 +	}
 +
 +	return contents;
 +} //*/
 +
 +
 +//============================================================================
 +
 +
 +
 +/*
 +=================
 +ParseBrush
 +=================
 +* /
 +void ParseBrush (entity_t *mapent)
 +{
 +	mapbrush_t		*b;
 +	int			i,j, k;
 +	int			mt;
 +	side_t		*side, *s2;
 +	int			planenum;
 +	brush_texture_t	td;
 +#ifdef SIN
 +   textureref_t newref;
 +#endif
 +	int			planepts[3][3];
 +
 +	if (nummapbrushes == MAX_MAP_BRUSHES)
 +		Error ("nummapbrushes == MAX_MAP_BRUSHES");
 +
 +	b = &mapbrushes[nummapbrushes];
 +	b->original_sides = &brushsides[nummapbrushsides];
 +	b->entitynum = num_entities-1;
 +	b->brushnum = nummapbrushes - mapent->firstbrush;
 +
 +	do
 +	{
 +		if (!GetToken (true))
 +			break;
 +		if (!strcmp (token, "}") )
 +			break;
 +
 +		if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
 +			Error ("MAX_MAP_BRUSHSIDES");
 +		side = &brushsides[nummapbrushsides];
 +
 +		// read the three point plane definition
 +		for (i=0 ; i<3 ; i++)
 +		{
 +			if (i != 0)
 +				GetToken (true);
 +			if (strcmp (token, "(") )
 +				Error ("parsing brush");
 +			
 +			for (j=0 ; j<3 ; j++)
 +			{
 +				GetToken (false);
 +				planepts[i][j] = atoi(token);
 +			}
 +			
 +			GetToken (false);
 +			if (strcmp (token, ")") )
 +				Error ("parsing brush");
 +				
 +		}
 +
 +
 +		//
 +		// read the texturedef
 +		//
 +		GetToken (false);
 +		strcpy (td.name, token);
 +
 +		GetToken (false);
 +		td.shift[0] = atoi(token);
 +		GetToken (false);
 +		td.shift[1] = atoi(token);
 +		GetToken (false);
 +#ifdef SIN
 +		td.rotate = atof(token);	
 +#else
 +		td.rotate = atoi(token);	
 +#endif
 +		GetToken (false);
 +		td.scale[0] = atof(token);
 +		GetToken (false);
 +		td.scale[1] = atof(token);
 +
 +		// find default flags and values
 +		mt = FindMiptex (td.name);
 +#ifdef SIN
 +      // clear out the masks on newref
 +      memset(&newref,0,sizeof(newref));
 +      // copy over the name
 +      strcpy( newref.name, td.name );
 +
 +      ParseSurfaceInfo( &newref );
 +      MergeRefs( &bsp_textureref[mt], &newref, &td.tref );
 +      side->contents = td.tref.contents;
 +      side->surf = td.tref.flags;
 +#else
 +		td.flags = textureref[mt].flags;
 +		td.value = textureref[mt].value;
 +		side->contents = textureref[mt].contents;
 +		side->surf = td.flags = textureref[mt].flags;
 +
 +		if (TokenAvailable())
 +		{
 +			GetToken (false);
 +			side->contents = atoi(token);
 +			GetToken (false);
 +			side->surf = td.flags = atoi(token);
 +			GetToken (false);
 +			td.value = atoi(token);
 +		}
 +#endif
 +
 +		// translucent objects are automatically classified as detail
 +#ifdef SIN
 +		if ( td.tref.translucence > 0 )
 +#else
 +		if (side->surf & (SURF_TRANS33|SURF_TRANS66) )
 +#endif
 +			side->contents |= CONTENTS_DETAIL;
 +		if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (fulldetail)
 +			side->contents &= ~CONTENTS_DETAIL;
 +		if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) 
 +			| CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST)  ) )
 +			side->contents |= CONTENTS_SOLID;
 +
 +		// hints and skips are never detail, and have no content
 +		if (side->surf & (SURF_HINT|SURF_SKIP) )
 +		{
 +			side->contents = 0;
 +#ifndef SIN // I think this is a bug of some kind
 +			side->surf &= ~CONTENTS_DETAIL;
 +#endif
 +		}
 +
 +		//
 +		// find the plane number
 +		//
 +		planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]);
 +		if (planenum == -1)
 +		{
 +			printf ("Entity %i, Brush %i: plane with no normal\n"
 +				, b->entitynum, b->brushnum);
 +			continue;
 +		}
 +
 +		//
 +		// see if the plane has been used already
 +		//
 +		for (k=0 ; k<b->numsides ; k++)
 +		{
 +			s2 = b->original_sides + k;
 +			if (s2->planenum == planenum)
 +			{
 +				printf ("Entity %i, Brush %i: duplicate plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +			if ( s2->planenum == (planenum^1) )
 +			{
 +				printf ("Entity %i, Brush %i: mirrored plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +		}
 +		if (k != b->numsides)
 +			continue;		// duplicated
 +
 +		//
 +		// keep this side
 +		//
 +
 +		side = b->original_sides + b->numsides;
 +		side->planenum = planenum;
 +#ifdef SIN
 +		side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],
 +			&td, vec3_origin, &newref);
 +      // 
 +      // save off lightinfo
 +      //
 +		side->lightinfo = LightinfoForBrushTexture ( &td );
 +#else
 +		side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],
 +			&td, vec3_origin);
 +
 +#endif
 +
 +		// save the td off in case there is an origin brush and we
 +		// have to recalculate the texinfo
 +		side_brushtextures[nummapbrushsides] = td;
 +#ifdef SIN
 +      // save off the merged tref for animating textures
 +		side_newrefs[nummapbrushsides] = newref;
 +#endif
 +
 +		nummapbrushsides++;
 +		b->numsides++;
 +	} while (1);
 +
 +	// get the content for the entire brush
 +	b->contents = Sin_BrushContents (b);
 +
 +	// allow detail brushes to be removed 
 +	if (nodetail && (b->contents & CONTENTS_DETAIL) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	}
 +
 +	// allow water brushes to be removed
 +	if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	}
 +
 +	// create windings for sides and bounds for brush
 +	MakeBrushWindings (b);
 +
 +	// brushes that will not be visible at all will never be
 +	// used as bsp splitters
 +	if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +	{
 +		c_clipbrushes++;
 +		for (i=0 ; i<b->numsides ; i++)
 +			b->original_sides[i].texinfo = TEXINFO_NODE;
 +	}
 +
 +	//
 +	// origin brushes are removed, but they set
 +	// the rotation origin for the rest of the brushes
 +	// in the entity.  After the entire entity is parsed,
 +	// the planenums and texinfos will be adjusted for
 +	// the origin brush
 +	//
 +	if (b->contents & CONTENTS_ORIGIN)
 +	{
 +		char	string[32];
 +		vec3_t	origin;
 +
 +		if (num_entities == 1)
 +		{
 +			Error ("Entity %i, Brush %i: origin brushes not allowed in world"
 +				, b->entitynum, b->brushnum);
 +			return;
 +		}
 +
 +		VectorAdd (b->mins, b->maxs, origin);
 +		VectorScale (origin, 0.5, origin);
 +
 +		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
 +		SetKeyValue (&entities[b->entitynum], "origin", string);
 +
 +		VectorCopy (origin, entities[b->entitynum].origin);
 +
 +		// don't keep this brush
 +		b->numsides = 0;
 +
 +		return;
 +	}
 +
 +	AddBrushBevels (b);
 +
 +	nummapbrushes++;
 +	mapent->numbrushes++;		
 +} //*/
 +
 +/*
 +================
 +MoveBrushesToWorld
 +
 +Takes all of the brushes from the current entity and
 +adds them to the world's brush list.
 +
 +Used by func_group and func_areaportal
 +================
 +* /
 +void MoveBrushesToWorld (entity_t *mapent)
 +{
 +	int			newbrushes;
 +	int			worldbrushes;
 +	mapbrush_t	*temp;
 +	int			i;
 +
 +	// this is pretty gross, because the brushes are expected to be
 +	// in linear order for each entity
 +
 +	newbrushes = mapent->numbrushes;
 +	worldbrushes = entities[0].numbrushes;
 +
 +	temp = malloc(newbrushes*sizeof(mapbrush_t));
 +	memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
 +
 +#if	0		// let them keep their original brush numbers
 +	for (i=0 ; i<newbrushes ; i++)
 +		temp[i].entitynum = 0;
 +#endif
 +
 +	// make space to move the brushes (overlapped copy)
 +	memmove (mapbrushes + worldbrushes + newbrushes,
 +		mapbrushes + worldbrushes,
 +		sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
 +
 +	// copy the new brushes down
 +	memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
 +
 +	// fix up indexes
 +	entities[0].numbrushes += newbrushes;
 +	for (i=1 ; i<num_entities ; i++)
 +		entities[i].firstbrush += newbrushes;
 +	free (temp);
 +
 +	mapent->numbrushes = 0;
 +} //*/
 +
 +/*
 +================
 +ParseMapEntity
 +================
 +* /
 +qboolean	Sin_ParseMapEntity (void)
 +{
 +	entity_t	*mapent;
 +	epair_t		*e;
 +	side_t		*s;
 +	int			i, j;
 +	int			startbrush, startsides;
 +	vec_t		newdist;
 +	mapbrush_t	*b;
 +
 +	if (!GetToken (true))
 +		return false;
 +
 +	if (strcmp (token, "{") )
 +		Error ("ParseEntity: { not found");
 +	
 +	if (num_entities == MAX_MAP_ENTITIES)
 +		Error ("num_entities == MAX_MAP_ENTITIES");
 +
 +	startbrush = nummapbrushes;
 +	startsides = nummapbrushsides;
 +
 +	mapent = &entities[num_entities];
 +	num_entities++;
 +	memset (mapent, 0, sizeof(*mapent));
 +	mapent->firstbrush = nummapbrushes;
 +	mapent->numbrushes = 0;
 +//	mapent->portalareas[0] = -1;
 +//	mapent->portalareas[1] = -1;
 +
 +	do
 +	{
 +		if (!GetToken (true))
 +			Error ("ParseEntity: EOF without closing brace");
 +		if (!strcmp (token, "}") )
 +			break;
 +		if (!strcmp (token, "{") )
 +			ParseBrush (mapent);
 +		else
 +		{
 +			e = ParseEpair ();
 +#ifdef SIN
 +         //HACK HACK HACK
 +         // MED Gotta do this here
 +         if ( !stricmp(e->key, "surfacefile") )
 +            {
 +            if (!surfacefile[0])
 +               {
 +               strcpy( surfacefile, e->value );
 +               }
 +		      printf ("--- ParseSurfaceFile ---\n");
 +		      printf ("Surface script: %s\n", surfacefile);
 +		      if (!ParseSurfaceFile(surfacefile))
 +               {
 +		         Error ("Script file not found: %s\n", surfacefile);
 +               }
 +            }
 +#endif
 +			e->next = mapent->epairs;
 +			mapent->epairs = e;
 +		}
 +	} while (1);
 +
 +#ifdef SIN
 +    if (!(strlen(ValueForKey(mapent, "origin")))  && ((num_entities-1) != 0))
 +        {
 +        mapbrush_t     *brush;
 +        vec3_t		    origin;
 +  	    char		    string[32];
 +        vec3_t          mins, maxs;
 +        int			    start, end;
 +        // Calculate bounds
 +
 +        start = mapent->firstbrush;
 +	    end = start + mapent->numbrushes;
 +	    ClearBounds (mins, maxs);
 +
 +	    for (j=start ; j<end ; j++)
 +            {
 +	        brush = &mapbrushes[j];
 +		    if (!brush->numsides)
 +			    continue;	// not a real brush (origin brush) - shouldn't happen
 +		    AddPointToBounds (brush->mins, mins, maxs);
 +		    AddPointToBounds (brush->maxs, mins, maxs);
 +            }
 +
 +        // Set the origin to be the centroid of the entity.
 +        VectorAdd ( mins, maxs, origin);
 +		VectorScale( origin, 0.5f, origin );
 +
 +		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
 +		SetKeyValue ( mapent, "origin", string);
 +//        qprintf("Setting origin to %s\n",string);
 +        }
 +#endif
 +
 +	GetVectorForKey (mapent, "origin", mapent->origin);
 +
 +#ifdef SIN
 +	if (
 +         (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) ||
 +         (!strcmp ("func_group", ValueForKey (mapent, "classname"))) ||
 +      	(!strcmp ("detail", ValueForKey (mapent, "classname")) && !entitydetails)
 +      )
 +      {
 +      VectorClear( mapent->origin );
 +      }
 +#endif
 +
 +	//
 +	// if there was an origin brush, offset all of the planes and texinfo
 +	//
 +	if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
 +	{
 +		for (i=0 ; i<mapent->numbrushes ; i++)
 +		{
 +			b = &mapbrushes[mapent->firstbrush + i];
 +			for (j=0 ; j<b->numsides ; j++)
 +			{
 +				s = &b->original_sides[j];
 +				newdist = mapplanes[s->planenum].dist -
 +					DotProduct (mapplanes[s->planenum].normal, mapent->origin);
 +				s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
 +#ifdef SIN
 +				s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum],
 +					&side_brushtextures[s-brushsides], mapent->origin, &side_newrefs[s-brushsides]);
 +            // 
 +            // save off lightinfo
 +            //
 +            s->lightinfo = LightinfoForBrushTexture (	&side_brushtextures[s-brushsides] );
 +#else
 +				s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum],
 +					&side_brushtextures[s-brushsides], mapent->origin);
 +#endif
 +			}
 +			MakeBrushWindings (b);
 +		}
 +	}
 +
 +	// group entities are just for editor convenience
 +	// toss all brushes into the world entity
 +	if (!strcmp ("func_group", ValueForKey (mapent, "classname")))
 +	{
 +		MoveBrushesToWorld (mapent);
 +		mapent->numbrushes = 0;
 +		mapent->wasdetail = true;
 +      FreeValueKeys( mapent );
 +		return true;
 +	}
 +#ifdef SIN
 +	// detail entities are just for editor convenience
 +	// toss all brushes into the world entity as detail brushes
 +	if (!strcmp ("detail", ValueForKey (mapent, "classname")) && !entitydetails)
 +	{
 +		for (i=0 ; i<mapent->numbrushes ; i++)
 +		{
 +         int j;
 +         side_t * s;
 +			b = &mapbrushes[mapent->firstbrush + i];
 +   	   if (nodetail)
 +            {
 +            b->numsides = 0;
 +            continue;
 +            }
 +         if (!fulldetail)
 +            {
 +   	      // set the contents for the entire brush
 +	         b->contents |= CONTENTS_DETAIL;
 +			   // set the contents in the sides as well
 +			   for (j=0, s=b->original_sides ; j<b->numsides ; j++,s++)
 +		   	   {
 +               s->contents |= CONTENTS_DETAIL;
 +	   	   	}
 +            }
 +         else
 +            {
 +   	      // set the contents for the entire brush
 +	         b->contents |= CONTENTS_SOLID;
 +			   // set the contents in the sides as well
 +			   for (j=0, s=b->original_sides ; j<b->numsides ; j++,s++)
 +		   	   {
 +               s->contents |= CONTENTS_SOLID;
 +	   	   	}
 +            }
 +		}
 +		MoveBrushesToWorld (mapent);
 +		mapent->wasdetail = true;
 +      FreeValueKeys( mapent );
 +      // kill off the entity
 +   	// num_entities--;
 +		return true;
 +	}
 +#endif
 +
 +	// areaportal entities move their brushes, but don't eliminate
 +	// the entity
 +	if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
 +	{
 +		char	str[128];
 +
 +		if (mapent->numbrushes != 1)
 +			Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
 +
 +		b = &mapbrushes[nummapbrushes-1];
 +		b->contents = CONTENTS_AREAPORTAL;
 +		c_areaportals++;
 +		mapent->areaportalnum = c_areaportals;
 +		// set the portal number as "style"
 +		sprintf (str, "%i", c_areaportals);
 +		SetKeyValue (mapent, "style", str);
 +		MoveBrushesToWorld (mapent);
 +		return true;
 +	}
 +
 +	return true;
 +} //end of the function Sin_ParseMapEntity */
 +
 +//===================================================================
 +
 +/*
 +================
 +LoadMapFile
 +================
 +* /
 +void Sin_LoadMapFile (char *filename)
 +{		
 +	int		i;
 +#ifdef SIN
 +   int num_detailsides=0;
 +   int num_detailbrushes=0;
 +   int num_worldsides=0;
 +   int num_worldbrushes=0;
 +   int      j,k;
 +#endif
 +
 +	qprintf ("--- LoadMapFile ---\n");
 +
 +	LoadScriptFile (filename);
 +
 +	nummapbrushsides = 0;
 +	num_entities = 0;
 +	
 +	while (ParseMapEntity ())
 +	{
 +	}
 +
 +	ClearBounds (map_mins, map_maxs);
 +	for (i=0 ; i<entities[0].numbrushes ; i++)
 +	{
 +		if (mapbrushes[i].mins[0] > 4096)
 +			continue;	// no valid points
 +		AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
 +		AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
 +	}
 +#ifdef SIN
 +   for (j=0;  j<num_entities; j++)
 +      {
 +	   for (i=0 ; i<entities[j].numbrushes ; i++)
 +	      {
 +         side_t * s;
 +         mapbrush_t *b;
 +			b = &mapbrushes[entities[j].firstbrush + i];
 +         if (b->numsides && b->contents & CONTENTS_DETAIL)
 +            num_detailbrushes++;
 +         else if (b->numsides)
 +            num_worldbrushes++;
 +			for (k=0, s=b->original_sides ; k<b->numsides ; k++,s++)
 +			   {
 +            if (s->contents & CONTENTS_DETAIL)
 +               num_detailsides++;
 +            else
 +               num_worldsides++;
 +			   }
 +   	   }
 +      }
 +#endif
 +
 +	qprintf ("%5i brushes\n", nummapbrushes);
 +	qprintf ("%5i clipbrushes\n", c_clipbrushes);
 +	qprintf ("%5i total sides\n", nummapbrushsides);
 +	qprintf ("%5i boxbevels\n", c_boxbevels);
 +	qprintf ("%5i edgebevels\n", c_edgebevels);
 +	qprintf ("%5i entities\n", num_entities);
 +	qprintf ("%5i planes\n", nummapplanes);
 +	qprintf ("%5i areaportals\n", c_areaportals);
 +	qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
 +		map_maxs[0],map_maxs[1],map_maxs[2]);
 +#ifdef SIN
 +	qprintf ("%5i detailbrushes\n", num_detailbrushes);
 +	qprintf ("%5i worldbrushes\n", num_worldbrushes);
 +	qprintf ("%5i detailsides\n", num_detailsides);
 +	qprintf ("%5i worldsides\n", num_worldsides);
 +#endif
 +
 +} //end of the function Sin_LoadMap */
 +
 +
 +#ifdef ME		//Begin MAP loading from BSP file
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_CreateMapTexinfo(void)
 +{
 +	int i;
 +	vec_t defaultvec[4] = {1, 0, 0, 0};
 +
 +	memcpy(map_texinfo[0].vecs[0], defaultvec, sizeof(defaultvec));
 +	memcpy(map_texinfo[0].vecs[1], defaultvec, sizeof(defaultvec));
 +	map_texinfo[0].flags = 0;
 +	map_texinfo[0].value = 0;
 +	strcpy(map_texinfo[0].texture, "generic/misc/red");	//no texture
 +	map_texinfo[0].nexttexinfo = -1;
 +	for (i = 1; i < sin_numtexinfo; i++)
 +	{
 +		memcpy(map_texinfo[i].vecs, sin_texinfo[i].vecs, sizeof(float) * 2 * 4);
 +		map_texinfo[i].flags = sin_texinfo[i].flags;
 +		map_texinfo[i].value = 0;
 +		strcpy(map_texinfo[i].texture, sin_texinfo[i].texture);
 +		map_texinfo[i].nexttexinfo = -1;
 +	} //end for
 +} //end of the function Sin_CreateMapTexinfo
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_SetLeafBrushesModelNumbers(int leafnum, int modelnum)
 +{
 +	int i, brushnum;
 +	sin_dleaf_t *leaf;
 +
 +	leaf = &sin_dleafs[leafnum];
 +	for (i = 0; i < leaf->numleafbrushes; i++)
 +	{
 +		brushnum = sin_dleafbrushes[leaf->firstleafbrush + i];
 +		brushmodelnumbers[brushnum] = modelnum;
 +		dbrushleafnums[brushnum] = leafnum;
 +	} //end for
 +} //end of the function Sin_SetLeafBrushesModelNumbers
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_InitNodeStack(void)
 +{
 +	nodestackptr = nodestack;
 +	nodestacksize = 0;
 +} //end of the function Sin_InitNodeStack
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_PushNodeStack(int num)
 +{
 +	*nodestackptr = num;
 +	nodestackptr++;
 +	nodestacksize++;
 +	//
 +	if (nodestackptr >= &nodestack[NODESTACKSIZE])
 +	{
 +		Error("Sin_PushNodeStack: stack overflow\n");
 +	} //end if
 +} //end of the function Sin_PushNodeStack
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int Sin_PopNodeStack(void)
 +{
 +	//if the stack is empty
 +	if (nodestackptr <= nodestack) return -1;
 +	//decrease stack pointer
 +	nodestackptr--;
 +	nodestacksize--;
 +	//return the top value from the stack
 +	return *nodestackptr;
 +} //end of the function Sin_PopNodeStack
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_SetBrushModelNumbers(entity_t *mapent)
 +{
 +	int n, pn;
 +	int leafnum;
 +
 +	//
 +	Sin_InitNodeStack();
 +	//head node (root) of the bsp tree
 +	n = sin_dmodels[mapent->modelnum].headnode;
 +	pn = 0;
 +	
 +	do
 +	{
 +		//if we are in a leaf (negative node number)
 +		if (n < 0)
 +		{
 +			//number of the leaf
 +			leafnum = (-n) - 1;
 +			//set the brush numbers
 +			Sin_SetLeafBrushesModelNumbers(leafnum, mapent->modelnum);
 +			//walk back into the tree to find a second child to continue with
 +			for (pn = Sin_PopNodeStack(); pn >= 0; n = pn, pn = Sin_PopNodeStack())
 +			{
 +				//if we took the first child at the parent node
 +				if (sin_dnodes[pn].children[0] == n) break;
 +			} //end for
 +			//if the stack wasn't empty (if not processed whole tree)
 +			if (pn >= 0)
 +			{
 +				//push the parent node again
 +				Sin_PushNodeStack(pn);
 +				//we proceed with the second child of the parent node
 +				n = sin_dnodes[pn].children[1];
 +			} //end if
 +		} //end if
 +		else
 +		{
 +			//push the current node onto the stack
 +			Sin_PushNodeStack(n);
 +			//walk forward into the tree to the first child
 +			n = sin_dnodes[n].children[0];
 +		} //end else
 +	} while(pn >= 0);
 +} //end of the function Sin_SetBrushModelNumbers
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_BSPBrushToMapBrush(sin_dbrush_t *bspbrush, entity_t *mapent)
 +{
 +	mapbrush_t *b;
 +	int i, k, n;
 +	side_t *side, *s2;
 +	int planenum;
 +	sin_dbrushside_t *bspbrushside;
 +	sin_dplane_t *bspplane;
 +
 +	if (nummapbrushes >= MAX_MAPFILE_BRUSHES)
 +		Error ("nummapbrushes >= MAX_MAPFILE_BRUSHES");
 +
 +	b = &mapbrushes[nummapbrushes];
 +	b->original_sides = &brushsides[nummapbrushsides];
 +	b->entitynum = mapent-entities;
 +	b->brushnum = nummapbrushes - mapent->firstbrush;
 +	b->leafnum = dbrushleafnums[bspbrush - sin_dbrushes];
 +
 +	for (n = 0; n < bspbrush->numsides; n++)
 +	{
 +		//pointer to the bsp brush side
 +		bspbrushside = &sin_dbrushsides[bspbrush->firstside + n];
 +
 +		if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES)
 +		{
 +			Error ("MAX_MAPFILE_BRUSHSIDES");
 +		} //end if
 +		//pointer to the map brush side
 +		side = &brushsides[nummapbrushsides];
 +		//if the BSP brush side is textured
 +		if (sin_dbrushsidetextured[bspbrush->firstside + n]) side->flags |= SFL_TEXTURED;
 +		else side->flags &= ~SFL_TEXTURED;
 +		//ME: can get side contents and surf directly from BSP file
 +		side->contents = bspbrush->contents;
 +		//if the texinfo is TEXINFO_NODE
 +		if (bspbrushside->texinfo < 0) side->surf = 0;
 +		else side->surf = sin_texinfo[bspbrushside->texinfo].flags;
 +
 +		// translucent objects are automatically classified as detail
 +		if (side->surf & (SURF_TRANS33|SURF_TRANS66) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +			side->contents |= CONTENTS_DETAIL;
 +		if (fulldetail)
 +			side->contents &= ~CONTENTS_DETAIL;
 +		if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) 
 +			| CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST)  ) )
 +			side->contents |= CONTENTS_SOLID;
 +
 +		// hints and skips are never detail, and have no content
 +		if (side->surf & (SURF_HINT|SURF_SKIP) )
 +		{
 +			side->contents = 0;
 +			side->surf &= ~CONTENTS_DETAIL;
 +		}
 +
 +		//ME: get a plane for this side
 +		bspplane = &sin_dplanes[bspbrushside->planenum];
 +		planenum = FindFloatPlane(bspplane->normal, bspplane->dist);
 +		//
 +		// see if the plane has been used already
 +		//
 +		//ME: this really shouldn't happen!!!
 +		//ME: otherwise the bsp file is corrupted??
 +		//ME: still it seems to happen, maybe Johny Boy's
 +		//ME: brush bevel adding is crappy ?
 +		for (k = 0; k < b->numsides; k++)
 +		{
 +			s2 = b->original_sides + k;
 +			if (s2->planenum == planenum)
 +			{
 +				Log_Print("Entity %i, Brush %i: duplicate plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +			if ( s2->planenum == (planenum^1) )
 +			{
 +				Log_Print("Entity %i, Brush %i: mirrored plane\n"
 +					, b->entitynum, b->brushnum);
 +				break;
 +			}
 +		}
 +		if (k != b->numsides)
 +			continue;		// duplicated
 +
 +		//
 +		// keep this side
 +		//
 +		//ME: reset pointer to side, why? hell I dunno (pointer is set above already)
 +		side = b->original_sides + b->numsides;
 +		//ME: store the plane number
 +		side->planenum = planenum;
 +		//ME: texinfo is already stored when bsp is loaded
 +		//NOTE: check for TEXINFO_NODE, otherwise crash in Sin_BrushContents
 +		if (bspbrushside->texinfo < 0) side->texinfo = 0;
 +		else side->texinfo = bspbrushside->texinfo;
 +
 +		// save the td off in case there is an origin brush and we
 +		// have to recalculate the texinfo
 +		// ME: don't need to recalculate because it's already done
 +		//     (for non-world entities) in the BSP file
 +//		side_brushtextures[nummapbrushsides] = td;
 +
 +		nummapbrushsides++;
 +		b->numsides++;
 +	} //end for
 +
 +	// get the content for the entire brush
 +	b->contents = bspbrush->contents;
 +	Sin_BrushContents(b);
 +
 +	if (BrushExists(b))
 +	{
 +		c_squattbrushes++;
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	//if we're creating AAS
 +	if (create_aas)
 +	{
 +		//create the AAS brushes from this brush, don't add brush bevels
 +		AAS_CreateMapBrushes(b, mapent, false);
 +		return;
 +	} //end if
 +
 +	// allow detail brushes to be removed 
 +	if (nodetail && (b->contents & CONTENTS_DETAIL) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	// allow water brushes to be removed
 +	if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
 +	{
 +		b->numsides = 0;
 +		return;
 +	} //end if
 +
 +	// create windings for sides and bounds for brush
 +	MakeBrushWindings(b);
 +
 +	//mark brushes without winding or with a tiny window as bevels
 +	MarkBrushBevels(b);
 +
 +	// brushes that will not be visible at all will never be
 +	// used as bsp splitters
 +	if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
 +	{
 +			c_clipbrushes++;
 +		for (i = 0; i < b->numsides; i++)
 +			b->original_sides[i].texinfo = TEXINFO_NODE;
 +	} //end for
 +
 +	//
 +	// origin brushes are removed, but they set
 +	// the rotation origin for the rest of the brushes
 +	// in the entity.  After the entire entity is parsed,
 +	// the planenums and texinfos will be adjusted for
 +	// the origin brush
 +	//
 +	//ME: not needed because the entities in the BSP file already
 +	//    have an origin set
 +//	if (b->contents & CONTENTS_ORIGIN)
 +//	{
 +//		char	string[32];
 +//		vec3_t	origin;
 +//
 +//		if (num_entities == 1)
 +//		{
 +//			Error ("Entity %i, Brush %i: origin brushes not allowed in world"
 +//				, b->entitynum, b->brushnum);
 +//			return;
 +//		}
 +//
 +//		VectorAdd (b->mins, b->maxs, origin);
 +//		VectorScale (origin, 0.5, origin);
 +//
 +//		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
 +//		SetKeyValue (&entities[b->entitynum], "origin", string);
 +//
 +//		VectorCopy (origin, entities[b->entitynum].origin);
 +//
 +//		// don't keep this brush
 +//		b->numsides = 0;
 +//
 +//		return;
 +//	}
 +
 +	//ME: the bsp brushes already have bevels, so we won't try to
 +	//    add them again (especially since Johny Boy's bevel adding might
 +	//    be crappy)
 +//	AddBrushBevels(b);
 +
 +	nummapbrushes++;
 +	mapent->numbrushes++;
 +} //end of the function Sin_BSPBrushToMapBrush
 +//===========================================================================
 +//===========================================================================
 +void Sin_ParseBSPBrushes(entity_t *mapent)
 +{
 +	int i, testnum = 0;
 +
 +	//give all the brushes that belong to this entity the number of the
 +	//BSP model used by this entity
 +	Sin_SetBrushModelNumbers(mapent);
 +	//now parse all the brushes with the correct mapent->modelnum
 +	for (i = 0; i < sin_numbrushes; i++)
 +	{
 +		if (brushmodelnumbers[i] == mapent->modelnum)
 +		{
 +			testnum++;
 +			Sin_BSPBrushToMapBrush(&sin_dbrushes[i], mapent);
 +		} //end if
 +	} //end for
 +} //end of the function Sin_ParseBSPBrushes
 +//===========================================================================
 +//===========================================================================
 +qboolean Sin_ParseBSPEntity(int entnum)
 +{
 +	entity_t	*mapent;
 +	char *model;
 +	int startbrush, startsides;
 +
 +	startbrush = nummapbrushes;
 +	startsides = nummapbrushsides;
 +
 +	mapent = &entities[entnum];//num_entities];
 +	mapent->firstbrush = nummapbrushes;
 +	mapent->numbrushes = 0;
 +	mapent->modelnum = -1;	//-1 = no model
 +
 +	model = ValueForKey(mapent, "model");
 +	if (model && *model == '*')
 +	{
 +		mapent->modelnum = atoi(&model[1]);
 +		//Log_Print("model = %s\n", model);
 +		//Log_Print("mapent->modelnum = %d\n", mapent->modelnum);
 +	} //end if
 +
 +	GetVectorForKey(mapent, "origin", mapent->origin);
 +
 +	//if this is the world entity it has model number zero
 +	//the world entity has no model key
 +	if (!strcmp("worldspawn", ValueForKey(mapent, "classname")))
 +	{
 +		mapent->modelnum = 0;
 +	} //end if
 +	//if the map entity has a BSP model (a modelnum of -1 is used for
 +	//entities that aren't using a BSP model)
 +	if (mapent->modelnum >= 0)
 +	{
 +		//parse the bsp brushes
 +		Sin_ParseBSPBrushes(mapent);
 +	} //end if
 +	//
 +	//the origin of the entity is already taken into account
 +	//
 +	//func_group entities can't be in the bsp file
 +	//
 +	//check out the func_areaportal entities
 +	if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
 +	{
 +		c_areaportals++;
 +		mapent->areaportalnum = c_areaportals;
 +		return true;
 +	} //end if
 +	return true;
 +} //end of the function Sin_ParseBSPEntity
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void Sin_LoadMapFromBSP(char *filename, int offset, int length)
 +{
 +	int i;
 +
 +	Log_Print("-- Sin_LoadMapFromBSP --\n");
 +	//loaded map type
 +	loadedmaptype = MAPTYPE_SIN;
 +
 +	Log_Print("Loading map from %s...\n", filename);
 +	//load the bsp file
 +	Sin_LoadBSPFile(filename, offset, length);
 +
 +	//create an index from bsp planes to map planes
 +	//DPlanes2MapPlanes();
 +	//clear brush model numbers
 +	for (i = 0; i < MAX_MAPFILE_BRUSHES; i++)
 +		brushmodelnumbers[i] = -1;
 +
 +	nummapbrushsides = 0;
 +	num_entities = 0;
 +
 +	Sin_ParseEntities();
 +	//
 +	for (i = 0; i < num_entities; i++)
 +	{
 +		Sin_ParseBSPEntity(i);
 +	} //end for
 +
 +	//get the map mins and maxs from the world model
 +	ClearBounds(map_mins, map_maxs);
 +	for (i = 0; i < entities[0].numbrushes; i++)
 +	{
 +		if (mapbrushes[i].mins[0] > 4096)
 +			continue;	//no valid points
 +		AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
 +		AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
 +	} //end for
 +	//
 +	Sin_CreateMapTexinfo();
 +} //end of the function Sin_LoadMapFromBSP
 +
 +void Sin_ResetMapLoading(void)
 +{
 +	//reset for map loading from bsp
 +	memset(nodestack, 0, NODESTACKSIZE * sizeof(int));
 +	nodestackptr = NULL;
 +	nodestacksize = 0;
 +	memset(brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof(int));
 +} //end of the function Sin_ResetMapLoading
 +
 +//End MAP loading from BSP file
 +
 +#endif //ME
 diff --git a/code/bspc/nodraw.c b/code/bspc/nodraw.c new file mode 100755 index 0000000..b5442dc --- /dev/null +++ b/code/bspc/nodraw.c @@ -0,0 +1,47 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +
 +vec3_t draw_mins, draw_maxs;
 +qboolean	drawflag;
 +
 +void Draw_ClearWindow (void)
 +{
 +}
 +
 +//============================================================
 +
 +#define	GLSERV_PORT	25001
 +
 +
 +void GLS_BeginScene (void)
 +{
 +}
 +
 +void GLS_Winding (winding_t *w, int code)
 +{
 +}
 +
 +void GLS_EndScene (void)
 +{
 +}
 diff --git a/code/bspc/portals.c b/code/bspc/portals.c new file mode 100755 index 0000000..3d3389d --- /dev/null +++ b/code/bspc/portals.c @@ -0,0 +1,1297 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_mem.h"
 +
 +int		c_active_portals;
 +int		c_peak_portals;
 +int		c_boundary;
 +int		c_boundary_sides;
 +int		c_portalmemory;
 +
 +//portal_t *portallist = NULL;
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +portal_t *AllocPortal (void)
 +{
 +	portal_t	*p;
 +	
 +	p = GetMemory(sizeof(portal_t));
 +	memset (p, 0, sizeof(portal_t));
 +
 +	if (numthreads == 1)
 +	{
 +		c_active_portals++;
 +		if (c_active_portals > c_peak_portals)
 +		{
 +			c_peak_portals = c_active_portals;
 +		} //end if
 +		c_portalmemory += MemorySize(p);	
 +	} //end if
 +
 +//	p->nextportal = portallist;
 +//	portallist = p;
 +	
 +	return p;
 +} //end of the function AllocPortal
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FreePortal (portal_t *p)
 +{
 +	if (p->winding) FreeWinding(p->winding);
 +	if (numthreads == 1)
 +	{
 +		c_active_portals--;
 +		c_portalmemory -= MemorySize(p);
 +	} //end if
 +	FreeMemory(p);
 +} //end of the function FreePortal
 +//===========================================================================
 +// Returns the single content bit of the
 +// strongest visible content present
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int VisibleContents (int contents)
 +{
 +	int		i;
 +
 +	for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
 +		if (contents & i )
 +			return i;
 +
 +	return 0;
 +} //end of the function VisibleContents
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int ClusterContents (node_t *node)
 +{
 +	int		c1, c2, c;
 +
 +	if (node->planenum == PLANENUM_LEAF)
 +		return node->contents;
 +
 +	c1 = ClusterContents(node->children[0]);
 +	c2 = ClusterContents(node->children[1]);
 +	c = c1|c2;
 +
 +	// a cluster may include some solid detail areas, but
 +	// still be seen into
 +	if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
 +		c &= ~CONTENTS_SOLID;
 +	return c;
 +} //end of the function ClusterContents
 +
 +//===========================================================================
 +// Returns true if the portal is empty or translucent, allowing
 +// the PVS calculation to see through it.
 +// The nodes on either side of the portal may actually be clusters,
 +// not leaves, so all contents should be ored together
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean Portal_VisFlood (portal_t *p)
 +{
 +	int		c1, c2;
 +
 +	if (!p->onnode)
 +		return false;	// to global outsideleaf
 +
 +	c1 = ClusterContents(p->nodes[0]);
 +	c2 = ClusterContents(p->nodes[1]);
 +
 +	if (!VisibleContents (c1^c2))
 +		return true;
 +
 +	if (c1 & (CONTENTS_Q2TRANSLUCENT|CONTENTS_DETAIL))
 +		c1 = 0;
 +	if (c2 & (CONTENTS_Q2TRANSLUCENT|CONTENTS_DETAIL))
 +		c2 = 0;
 +
 +	if ( (c1|c2) & CONTENTS_SOLID )
 +		return false;		// can't see through solid
 +
 +	if (! (c1 ^ c2))
 +		return true;		// identical on both sides
 +
 +	if (!VisibleContents (c1^c2))
 +		return true;
 +	return false;
 +} //end of the function Portal_VisFlood
 +//===========================================================================
 +// The entity flood determines which areas are
 +// "outside" on the map, which are then filled in.
 +// Flowing from side s to side !s
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean Portal_EntityFlood (portal_t *p, int s)
 +{
 +	if (p->nodes[0]->planenum != PLANENUM_LEAF
 +		|| p->nodes[1]->planenum != PLANENUM_LEAF)
 +		Error ("Portal_EntityFlood: not a leaf");
 +
 +	// can never cross to a solid 
 +	if ( (p->nodes[0]->contents & CONTENTS_SOLID)
 +	|| (p->nodes[1]->contents & CONTENTS_SOLID) )
 +		return false;
 +
 +	// can flood through everything else
 +	return true;
 +}
 +
 +
 +//=============================================================================
 +
 +int		c_tinyportals;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
 +{
 +	if (p->nodes[0] || p->nodes[1])
 +		Error ("AddPortalToNode: allready included");
 +
 +	p->nodes[0] = front;
 +	p->next[0] = front->portals;
 +	front->portals = p;
 +	
 +	p->nodes[1] = back;
 +	p->next[1] = back->portals;
 +	back->portals = p;
 +} //end of the function AddPortalToNodes
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void RemovePortalFromNode (portal_t *portal, node_t *l)
 +{
 +	portal_t	**pp, *t;
 +
 +	int s, i, n;
 +	portal_t *p;
 +	portal_t *portals[4096];
 +	
 +// remove reference to the current portal
 +	pp = &l->portals;
 +	while (1)
 +	{
 +		t = *pp;
 +		if (!t)
 +			Error ("RemovePortalFromNode: portal not in leaf");	
 +
 +		if ( t == portal )
 +			break;
 +
 +		if (t->nodes[0] == l)
 +			pp = &t->next[0];
 +		else if (t->nodes[1] == l)
 +			pp = &t->next[1];
 +		else
 +			Error ("RemovePortalFromNode: portal not bounding leaf");
 +	}
 +	
 +	if (portal->nodes[0] == l)
 +	{
 +		*pp = portal->next[0];
 +		portal->nodes[0] = NULL;
 +	} //end if
 +	else if (portal->nodes[1] == l)
 +	{
 +		*pp = portal->next[1];	
 +		portal->nodes[1] = NULL;
 +	} //end else if
 +	else
 +	{
 +		Error("RemovePortalFromNode: mislinked portal");
 +	} //end else
 +//#ifdef ME
 +	n = 0;
 +	for (p = l->portals; p; p = p->next[s])
 +	{
 +		for (i = 0; i < n; i++)
 +		{
 +			if (p == portals[i]) Error("RemovePortalFromNode: circular linked\n");
 +		} //end for
 +		if (p->nodes[0] != l && p->nodes[1] != l)
 +		{
 +			Error("RemovePortalFromNodes: portal does not belong to node\n");
 +		} //end if
 +		portals[n] = p;
 +		s = (p->nodes[1] == l);
 +//		if (++n >= 4096) Error("RemovePortalFromNode: more than 4096 portals\n");
 +	} //end for
 +//#endif
 +} //end of the function RemovePortalFromNode
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void PrintPortal (portal_t *p)
 +{
 +	int			i;
 +	winding_t	*w;
 +	
 +	w = p->winding;
 +	for (i=0 ; i<w->numpoints ; i++)
 +		printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
 +		, w->p[i][1], w->p[i][2]);
 +} //end of the function PrintPortal
 +//===========================================================================
 +// The created portals will face the global outside_node
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#define	SIDESPACE	8
 +
 +void MakeHeadnodePortals (tree_t *tree)
 +{
 +	vec3_t		bounds[2];
 +	int			i, j, n;
 +	portal_t	*p, *portals[6];
 +	plane_t		bplanes[6], *pl;
 +	node_t *node;
 +
 +	node = tree->headnode;
 +
 +// pad with some space so there will never be null volume leaves
 +	for (i=0 ; i<3 ; i++)
 +	{
 +		bounds[0][i] = tree->mins[i] - SIDESPACE;
 +		bounds[1][i] = tree->maxs[i] + SIDESPACE;
 +		if ( bounds[0][i] > bounds[1][i] ) {
 +			Error("empty BSP tree");
 +		}
 +	}
 +	
 +	tree->outside_node.planenum = PLANENUM_LEAF;
 +	tree->outside_node.brushlist = NULL;
 +	tree->outside_node.portals = NULL;
 +	tree->outside_node.contents = 0;
 +
 +	for (i=0 ; i<3 ; i++)
 +		for (j=0 ; j<2 ; j++)
 +		{
 +			n = j*3 + i;
 +
 +			p = AllocPortal ();
 +			portals[n] = p;
 +			
 +			pl = &bplanes[n];
 +			memset (pl, 0, sizeof(*pl));
 +			if (j)
 +			{
 +				pl->normal[i] = -1;
 +				pl->dist = -bounds[j][i];
 +			}
 +			else
 +			{
 +				pl->normal[i] = 1;
 +				pl->dist = bounds[j][i];
 +			}
 +			p->plane = *pl;
 +			p->winding = BaseWindingForPlane (pl->normal, pl->dist);
 +			AddPortalToNodes (p, node, &tree->outside_node);
 +		}
 +		
 +// clip the basewindings by all the other planes
 +	for (i=0 ; i<6 ; i++)
 +	{
 +		for (j=0 ; j<6 ; j++)
 +		{
 +			if (j == i) continue;
 +			ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
 +		} //end for
 +	} //end for
 +} //end of the function MakeHeadNodePortals
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#define	BASE_WINDING_EPSILON		0.001
 +#define	SPLIT_WINDING_EPSILON	0.001
 +
 +winding_t *BaseWindingForNode (node_t *node)
 +{
 +	winding_t	*w;
 +	node_t		*n;
 +	plane_t		*plane;
 +	vec3_t		normal;
 +	vec_t		dist;
 +
 +	w = BaseWindingForPlane (mapplanes[node->planenum].normal
 +		, mapplanes[node->planenum].dist);
 +
 +	// clip by all the parents
 +	for (n=node->parent ; n && w ; )
 +	{
 +		plane = &mapplanes[n->planenum];
 +
 +		if (n->children[0] == node)
 +		{	// take front
 +			ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
 +		}
 +		else
 +		{	// take back
 +			VectorSubtract (vec3_origin, plane->normal, normal);
 +			dist = -plane->dist;
 +			ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
 +		}
 +		node = n;
 +		n = n->parent;
 +	}
 +
 +	return w;
 +} //end of the function BaseWindingForNode
 +//===========================================================================
 +// create the new portal by taking the full plane winding for the cutting
 +// plane and clipping it by all of parents of this node
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean WindingIsTiny (winding_t *w);
 +
 +void MakeNodePortal (node_t *node)
 +{
 +	portal_t	*new_portal, *p;
 +	winding_t	*w;
 +	vec3_t		normal;
 +	float		dist;
 +	int			side;
 +
 +	w = BaseWindingForNode (node);
 +
 +	// clip the portal by all the other portals in the node
 +	for (p = node->portals; p && w; p = p->next[side])	
 +	{
 +		if (p->nodes[0] == node)
 +		{
 +			side = 0;
 +			VectorCopy (p->plane.normal, normal);
 +			dist = p->plane.dist;
 +		} //end if
 +		else if (p->nodes[1] == node)
 +		{
 +			side = 1;
 +			VectorSubtract (vec3_origin, p->plane.normal, normal);
 +			dist = -p->plane.dist;
 +		} //end else if
 +		else
 +		{
 +			Error ("MakeNodePortal: mislinked portal");
 +		} //end else
 +		ChopWindingInPlace (&w, normal, dist, 0.1);
 +	} //end for
 +
 +	if (!w)
 +	{
 +		return;
 +	} //end if
 +
 +	if (WindingIsTiny (w))
 +	{
 +		c_tinyportals++;
 +		FreeWinding(w);
 +		return;
 +	} //end if
 +
 +#ifdef DEBUG
 +/* //NOTE: don't use this winding ok check
 +	// all the invalid windings only have a degenerate edge
 +	if (WindingError(w))
 +	{
 +		Log_Print("MakeNodePortal: %s\n", WindingErrorString());
 +		FreeWinding(w);
 +		return;
 +	} //end if*/
 +#endif //DEBUG
 +
 +
 +	new_portal = AllocPortal();
 +	new_portal->plane = mapplanes[node->planenum];
 +
 +#ifdef ME
 +	new_portal->planenum = node->planenum;
 +#endif //ME
 +
 +	new_portal->onnode = node;
 +	new_portal->winding = w;
 +	AddPortalToNodes (new_portal, node->children[0], node->children[1]);
 +} //end of the function MakeNodePortal
 +//===========================================================================
 +// Move or split the portals that bound node so that the node's
 +// children have portals instead of node.
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void SplitNodePortals (node_t *node)
 +{
 +	portal_t	*p, *next_portal, *new_portal;
 +	node_t *f, *b, *other_node;
 +	int side;
 +	plane_t *plane;
 +	winding_t *frontwinding, *backwinding;
 +
 +	plane = &mapplanes[node->planenum];
 +	f = node->children[0];
 +	b = node->children[1];
 +
 +	for (p = node->portals ; p ; p = next_portal)	
 +	{
 +		if (p->nodes[0] == node) side = 0;
 +		else if (p->nodes[1] == node) side = 1;
 +		else Error ("SplitNodePortals: mislinked portal");
 +		next_portal = p->next[side];
 +
 +		other_node = p->nodes[!side];
 +		RemovePortalFromNode (p, p->nodes[0]);
 +		RemovePortalFromNode (p, p->nodes[1]);
 +
 +//
 +// cut the portal into two portals, one on each side of the cut plane
 +//
 +		ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
 +				SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
 +
 +		if (frontwinding && WindingIsTiny(frontwinding))
 +		{
 +			FreeWinding (frontwinding);
 +			frontwinding = NULL;
 +			c_tinyportals++;
 +		} //end if
 +
 +		if (backwinding && WindingIsTiny(backwinding))
 +		{
 +			FreeWinding (backwinding);
 +			backwinding = NULL;
 +			c_tinyportals++;
 +		} //end if
 +
 +#ifdef DEBUG
 +/* 	//NOTE: don't use this winding ok check
 +		// all the invalid windings only have a degenerate edge
 +		if (frontwinding && WindingError(frontwinding))
 +		{
 +			Log_Print("SplitNodePortals: front %s\n", WindingErrorString());
 +			FreeWinding(frontwinding);
 +			frontwinding = NULL;
 +		} //end if
 +		if (backwinding && WindingError(backwinding))
 +		{
 +			Log_Print("SplitNodePortals: back %s\n", WindingErrorString());
 +			FreeWinding(backwinding);
 +			backwinding = NULL;
 +		} //end if*/
 +#endif //DEBUG
 +
 +		if (!frontwinding && !backwinding)
 +		{	// tiny windings on both sides
 +			continue;
 +		}
 +
 +		if (!frontwinding)
 +		{
 +			FreeWinding (backwinding);
 +			if (side == 0) AddPortalToNodes (p, b, other_node);
 +			else AddPortalToNodes (p, other_node, b);
 +			continue;
 +		}
 +		if (!backwinding)
 +		{
 +			FreeWinding (frontwinding);
 +			if (side == 0) AddPortalToNodes (p, f, other_node);
 +			else AddPortalToNodes (p, other_node, f);
 +			continue;
 +		}
 +		
 +	// the winding is split
 +		new_portal = AllocPortal();
 +		*new_portal = *p;
 +		new_portal->winding = backwinding;
 +		FreeWinding (p->winding);
 +		p->winding = frontwinding;
 +
 +		if (side == 0)
 +		{
 +			AddPortalToNodes (p, f, other_node);
 +			AddPortalToNodes (new_portal, b, other_node);
 +		} //end if
 +		else
 +		{
 +			AddPortalToNodes (p, other_node, f);
 +			AddPortalToNodes (new_portal, other_node, b);
 +		} //end else
 +	}
 +
 +	node->portals = NULL;
 +} //end of the function SplitNodePortals
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void CalcNodeBounds (node_t *node)
 +{
 +	portal_t	*p;
 +	int			s;
 +	int			i;
 +
 +	// calc mins/maxs for both leaves and nodes
 +	ClearBounds (node->mins, node->maxs);
 +	for (p = node->portals ; p ; p = p->next[s])	
 +	{
 +		s = (p->nodes[1] == node);
 +		for (i=0 ; i<p->winding->numpoints ; i++)
 +			AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
 +	}
 +} //end of the function CalcNodeBounds
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int c_numportalizednodes;
 +
 +void MakeTreePortals_r (node_t *node)
 +{
 +	int		i;
 +
 +#ifdef ME
 +	qprintf("\r%6d", ++c_numportalizednodes);
 +	if (cancelconversion) return;
 +#endif //ME
 +
 +	CalcNodeBounds (node);
 +	if (node->mins[0] >= node->maxs[0])
 +	{
 +		Log_Print("WARNING: node without a volume\n");
 +	}
 +
 +	for (i=0 ; i<3 ; i++)
 +	{
 +		if (node->mins[i] < -MAX_MAP_BOUNDS || node->maxs[i] > MAX_MAP_BOUNDS)
 +		{
 +			Log_Print("WARNING: node with unbounded volume\n");
 +			break;
 +		}
 +	}
 +	if (node->planenum == PLANENUM_LEAF)
 +		return;
 +
 +	MakeNodePortal (node);
 +	SplitNodePortals (node);
 +
 +	MakeTreePortals_r (node->children[0]);
 +	MakeTreePortals_r (node->children[1]);
 +} //end of the function MakeTreePortals_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void MakeTreePortals(tree_t *tree)
 +{
 +
 +#ifdef ME
 +	Log_Print("---- Node Portalization ----\n");
 +	c_numportalizednodes = 0;
 +	c_portalmemory = 0;
 +	qprintf("%6d nodes portalized", c_numportalizednodes);
 +#endif //ME
 +
 +	MakeHeadnodePortals(tree);
 +	MakeTreePortals_r(tree->headnode);
 +
 +#ifdef ME
 +	qprintf("\n");
 +	Log_Write("%6d nodes portalized\r\n", c_numportalizednodes);
 +	Log_Print("%6d tiny portals\r\n", c_tinyportals);
 +	Log_Print("%6d KB of portal memory\r\n", c_portalmemory >> 10);
 +	Log_Print("%6i KB of winding memory\r\n", WindingMemory() >> 10);
 +#endif //ME
 +} //end of the function MakeTreePortals
 +
 +/*
 +=========================================================
 +
 +FLOOD ENTITIES
 +
 +=========================================================
 +*/
 +//#define P_NODESTACK
 +
 +node_t *p_firstnode;
 +node_t *p_lastnode;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +#ifdef P_NODESTACK
 +void P_AddNodeToList(node_t *node)
 +{
 +	node->next = p_firstnode;
 +	p_firstnode = node;
 +	if (!p_lastnode) p_lastnode = node;
 +} //end of the function P_AddNodeToList
 +#else //it's a node queue
 +//add the node to the end of the node list
 +void P_AddNodeToList(node_t *node)
 +{
 +	node->next = NULL;
 +	if (p_lastnode) p_lastnode->next = node;
 +	else p_firstnode = node;
 +	p_lastnode = node;
 +} //end of the function P_AddNodeToList
 +#endif //P_NODESTACK
 +//===========================================================================
 +// get the first node from the front of the node list
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +node_t *P_NextNodeFromList(void)
 +{
 +	node_t *node;
 +
 +	node = p_firstnode;
 +	if (p_firstnode) p_firstnode = p_firstnode->next;
 +	if (!p_firstnode) p_lastnode = NULL;
 +	return node;
 +} //end of the function P_NextNodeFromList
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FloodPortals(node_t *firstnode)
 +{
 +	node_t *node;
 +	portal_t *p;
 +	int s;
 +
 +	firstnode->occupied = 1;
 +	P_AddNodeToList(firstnode);
 +
 +	for (node = P_NextNodeFromList(); node; node = P_NextNodeFromList())
 +	{
 +		for (p = node->portals; p; p = p->next[s])
 +		{
 +			s = (p->nodes[1] == node);
 +			//if the node at the other side of the portal is occupied already
 +			if (p->nodes[!s]->occupied) continue;
 +			//if it isn't possible to flood through this portal
 +			if (!Portal_EntityFlood(p, s)) continue;
 +			//
 +			p->nodes[!s]->occupied = node->occupied + 1;
 +			//
 +			P_AddNodeToList(p->nodes[!s]);
 +		} //end for
 +	} //end for
 +} //end of the function FloodPortals
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +int numrec;
 +
 +void FloodPortals_r (node_t *node, int dist)
 +{
 +	portal_t *p;
 +	int s;
 +//	int i;
 +
 +	Log_Print("\r%6d", ++numrec);
 +
 +	if (node->occupied) Error("FloodPortals_r: node already occupied\n");
 +	if (!node)
 +	{
 +		Error("FloodPortals_r: NULL node\n");
 +	} //end if*/
 +	node->occupied = dist;
 +
 +	for (p = node->portals; p; p = p->next[s])
 +	{
 +		s = (p->nodes[1] == node);
 +		//if the node at the other side of the portal is occupied already
 +		if (p->nodes[!s]->occupied) continue;
 +		//if it isn't possible to flood through this portal
 +		if (!Portal_EntityFlood(p, s)) continue;
 +		//flood recursively through the current portal
 +		FloodPortals_r(p->nodes[!s], dist+1);
 +	} //end for
 +	Log_Print("\r%6d", --numrec);
 +} //end of the function FloodPortals_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
 +{
 +	node_t *node;
 +	vec_t	d;
 +	plane_t *plane;
 +
 +	//find the leaf to start in
 +	node = headnode;
 +	while(node->planenum != PLANENUM_LEAF)
 +	{
 +		if (node->planenum < 0 || node->planenum > nummapplanes)
 +		{
 +			Error("PlaceOccupant: invalid node->planenum\n");
 +		} //end if
 +		plane = &mapplanes[node->planenum];
 +		d = DotProduct(origin, plane->normal) - plane->dist;
 +		if (d >= 0) node = node->children[0];
 +		else node = node->children[1];
 +		if (!node)
 +		{
 +			Error("PlaceOccupant: invalid child %d\n", d < 0);
 +		} //end if
 +	} //end while
 +	//don't start in solid
 +//	if (node->contents == CONTENTS_SOLID)
 +	//ME: replaced because in LeafNode in brushbsp.c
 +	//    some nodes have contents solid with other contents
 +	if (node->contents & CONTENTS_SOLID) return false;
 +	//if the node is already occupied
 +	if (node->occupied) return false;
 +
 +	//place the occupant in the first leaf
 +	node->occupant = occupant;
 +
 +	numrec = 0;
 +//	Log_Print("%6d recurses", numrec);
 +//	FloodPortals_r(node, 1);
 +//	Log_Print("\n");
 +	FloodPortals(node);
 +
 +	return true;
 +} //end of the function PlaceOccupant
 +//===========================================================================
 +// Marks all nodes that can be reached by entites
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean FloodEntities (tree_t *tree)
 +{
 +	int i;
 +	int x, y;
 +	vec3_t origin;
 +	char *cl;
 +	qboolean inside;
 +	node_t *headnode;
 +
 +	headnode = tree->headnode;
 +	Log_Print("------ FloodEntities -------\n");
 +	inside = false;
 +	tree->outside_node.occupied = 0;
 +
 +	//start at entity 1 not the world ( = 0)
 +	for (i = 1; i < num_entities; i++)
 +	{
 +		GetVectorForKey(&entities[i], "origin", origin);
 +		if (VectorCompare(origin, vec3_origin)) continue;
 +
 +		cl = ValueForKey(&entities[i], "classname");
 +		origin[2] += 1;	//so objects on floor are ok
 +
 +//		Log_Print("flooding from entity %d: %s\n", i, cl);
 +		//nudge playerstart around if needed so clipping hulls allways
 +		//have a valid point
 +		if (!strcmp(cl, "info_player_start"))
 +		{
 +			for (x = -16; x <= 16; x += 16)
 +			{
 +				for (y = -16; y <= 16; y += 16)
 +				{
 +					origin[0] += x;
 +					origin[1] += y;
 +					if (PlaceOccupant(headnode, origin, &entities[i]))
 +					{
 +						inside = true;
 +						x = 999; //stop for this info_player_start
 +						break;
 +					} //end if
 +					origin[0] -= x;
 +					origin[1] -= y;
 +				} //end for
 +			} //end for
 +		} //end if
 +		else
 +		{
 +			if (PlaceOccupant(headnode, origin, &entities[i]))
 +			{
 +				inside = true;
 +			} //end if
 +		} //end else
 +	} //end for
 +
 +	if (!inside)
 +	{
 +		Log_Print("WARNING: no entities inside\n");
 +	} //end if
 +	else if (tree->outside_node.occupied)
 +	{
 +		Log_Print("WARNING: entity reached from outside\n");
 +	} //end else if
 +
 +	return (qboolean)(inside && !tree->outside_node.occupied);
 +} //end of the function FloodEntities
 +
 +/*
 +=========================================================
 +
 +FILL OUTSIDE
 +
 +=========================================================
 +*/
 +
 +int c_outside;
 +int c_inside;
 +int c_solid;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FillOutside_r (node_t *node)
 +{
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		FillOutside_r (node->children[0]);
 +		FillOutside_r (node->children[1]);
 +		return;
 +	} //end if
 +	// anything not reachable by an entity
 +	// can be filled away (by setting solid contents)
 +	if (!node->occupied)
 +	{
 +		if (!(node->contents & CONTENTS_SOLID))
 +		{
 +			c_outside++;
 +			node->contents |= CONTENTS_SOLID;
 +		} //end if
 +		else
 +		{
 +			c_solid++;
 +		} //end else
 +	} //end if
 +	else
 +	{
 +		c_inside++;
 +	} //end else
 +} //end of the function FillOutside_r
 +//===========================================================================
 +// Fill all nodes that can't be reached by entities
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FillOutside (node_t *headnode)
 +{
 +	c_outside = 0;
 +	c_inside = 0;
 +	c_solid = 0;
 +	Log_Print("------- FillOutside --------\n");
 +	FillOutside_r (headnode);
 +	Log_Print("%5i solid leaves\n", c_solid);
 +	Log_Print("%5i leaves filled\n", c_outside);
 +	Log_Print("%5i inside leaves\n", c_inside);
 +} //end of the function FillOutside
 +
 +/*
 +=========================================================
 +
 +FLOOD AREAS
 +
 +=========================================================
 +*/
 +
 +int		c_areas;
 +
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FloodAreas_r (node_t *node)
 +{
 +	portal_t	*p;
 +	int			s;
 +	bspbrush_t	*b;
 +	entity_t	*e;
 +
 +	if (node->contents == CONTENTS_AREAPORTAL)
 +	{
 +		// this node is part of an area portal
 +		b = node->brushlist;
 +		e = &entities[b->original->entitynum];
 +
 +		// if the current area has allready touched this
 +		// portal, we are done
 +		if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
 +			return;
 +
 +		// note the current area as bounding the portal
 +		if (e->portalareas[1])
 +		{
 +			Log_Print("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);
 +			return;
 +		}
 +		if (e->portalareas[0])
 +			e->portalareas[1] = c_areas;
 +		else
 +			e->portalareas[0] = c_areas;
 +
 +		return;
 +	} //end if
 +
 +	if (node->area)
 +		return;		// allready got it
 +	node->area = c_areas;
 +
 +	for (p=node->portals ; p ; p = p->next[s])
 +	{
 +		s = (p->nodes[1] == node);
 +#if 0
 +		if (p->nodes[!s]->occupied)
 +			continue;
 +#endif
 +		if (!Portal_EntityFlood (p, s))
 +			continue;
 +
 +		FloodAreas_r (p->nodes[!s]);
 +	} //end for
 +} //end of the function FloodAreas_r
 +//===========================================================================
 +// Just decend the tree, and for each node that hasn't had an
 +// area set, flood fill out from there
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FindAreas_r (node_t *node)
 +{
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		FindAreas_r (node->children[0]);
 +		FindAreas_r (node->children[1]);
 +		return;
 +	}
 +
 +	if (node->area)
 +		return;		// allready got it
 +
 +	if (node->contents & CONTENTS_SOLID)
 +		return;
 +
 +	if (!node->occupied)
 +		return;			// not reachable by entities
 +
 +	// area portals are allways only flooded into, never
 +	// out of
 +	if (node->contents == CONTENTS_AREAPORTAL)
 +		return;
 +
 +	c_areas++;
 +	FloodAreas_r (node);
 +} //end of the function FindAreas_r
 +//===========================================================================
 +// Just decend the tree, and for each node that hasn't had an
 +// area set, flood fill out from there
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void SetAreaPortalAreas_r (node_t *node)
 +{
 +	bspbrush_t	*b;
 +	entity_t	*e;
 +
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		SetAreaPortalAreas_r (node->children[0]);
 +		SetAreaPortalAreas_r (node->children[1]);
 +		return;
 +	} //end if
 +
 +	if (node->contents == CONTENTS_AREAPORTAL)
 +	{
 +		if (node->area)
 +			return;		// allready set
 +
 +		b = node->brushlist;
 +		e = &entities[b->original->entitynum];
 +		node->area = e->portalareas[0];
 +		if (!e->portalareas[1])
 +		{
 +			Log_Print("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);
 +			return;
 +		} //end if
 +	} //end if
 +} //end of the function SetAreaPortalAreas_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +/*
 +void EmitAreaPortals(node_t *headnode)
 +{
 +	int				i, j;
 +	entity_t		*e;
 +	dareaportal_t	*dp;
 +
 +	if (c_areas > MAX_MAP_AREAS)
 +		Error ("MAX_MAP_AREAS");
 +	numareas = c_areas+1;
 +	numareaportals = 1;		// leave 0 as an error
 +
 +	for (i=1 ; i<=c_areas ; i++)
 +	{
 +		dareas[i].firstareaportal = numareaportals;
 +		for (j=0 ; j<num_entities ; j++)
 +		{
 +			e = &entities[j];
 +			if (!e->areaportalnum)
 +				continue;
 +			dp = &dareaportals[numareaportals];
 +			if (e->portalareas[0] == i)
 +			{
 +				dp->portalnum = e->areaportalnum;
 +				dp->otherarea = e->portalareas[1];
 +				numareaportals++;
 +			} //end if
 +			else if (e->portalareas[1] == i)
 +			{
 +				dp->portalnum = e->areaportalnum;
 +				dp->otherarea = e->portalareas[0];
 +				numareaportals++;
 +			} //end else if
 +		} //end for
 +		dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;
 +	} //end for
 +
 +	Log_Print("%5i numareas\n", numareas);
 +	Log_Print("%5i numareaportals\n", numareaportals);
 +} //end of the function EmitAreaPortals
 +*/
 +//===========================================================================
 +// Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FloodAreas (tree_t *tree)
 +{
 +	Log_Print("--- FloodAreas ---\n");
 +	FindAreas_r (tree->headnode);
 +	SetAreaPortalAreas_r (tree->headnode);
 +	Log_Print("%5i areas\n", c_areas);
 +} //end of the function FloodAreas
 +//===========================================================================
 +// Finds a brush side to use for texturing the given portal
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void FindPortalSide (portal_t *p)
 +{
 +	int			viscontents;
 +	bspbrush_t	*bb;
 +	mapbrush_t	*brush;
 +	node_t		*n;
 +	int			i,j;
 +	int			planenum;
 +	side_t		*side, *bestside;
 +	float		dot, bestdot;
 +	plane_t		*p1, *p2;
 +
 +	// decide which content change is strongest
 +	// solid > lava > water, etc
 +	viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
 +	if (!viscontents)
 +		return;
 +
 +	planenum = p->onnode->planenum;
 +	bestside = NULL;
 +	bestdot = 0;
 +
 +	for (j=0 ; j<2 ; j++)
 +	{
 +		n = p->nodes[j];
 +		p1 = &mapplanes[p->onnode->planenum];
 +		for (bb=n->brushlist ; bb ; bb=bb->next)
 +		{
 +			brush = bb->original;
 +			if ( !(brush->contents & viscontents) )
 +				continue;
 +			for (i=0 ; i<brush->numsides ; i++)
 +			{
 +				side = &brush->original_sides[i];
 +				if (side->flags & SFL_BEVEL)
 +					continue;
 +				if (side->texinfo == TEXINFO_NODE)
 +					continue;		// non-visible
 +				if ((side->planenum&~1) == planenum)
 +				{	// exact match
 +					bestside = &brush->original_sides[i];
 +					goto gotit;
 +				} //end if
 +				// see how close the match is
 +				p2 = &mapplanes[side->planenum&~1];
 +				dot = DotProduct (p1->normal, p2->normal);
 +				if (dot > bestdot)
 +				{
 +					bestdot = dot;
 +					bestside = side;
 +				} //end if
 +			} //end for
 +		} //end for
 +	} //end for
 +
 +gotit:
 +	if (!bestside)
 +		Log_Print("WARNING: side not found for portal\n");
 +
 +	p->sidefound = true;
 +	p->side = bestside;
 +} //end of the function FindPortalSide
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void MarkVisibleSides_r (node_t *node)
 +{
 +	portal_t *p;
 +	int s;
 +
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		MarkVisibleSides_r (node->children[0]);
 +		MarkVisibleSides_r (node->children[1]);
 +		return;
 +	} //end if
 +
 +	// empty leaves are never boundary leaves
 +	if (!node->contents) return;
 +
 +	// see if there is a visible face
 +	for (p=node->portals ; p ; p = p->next[!s])
 +	{
 +		s = (p->nodes[0] == node);
 +		if (!p->onnode)
 +			continue;		// edge of world
 +		if (!p->sidefound)
 +			FindPortalSide (p);
 +		if (p->side)
 +			p->side->flags |= SFL_VISIBLE;
 +	} //end for
 +} //end of the function MarkVisibleSides_r
 +//===========================================================================
 +//
 +// Parameter:				-
 +// Returns:					-
 +// Changes Globals:		-
 +//===========================================================================
 +void MarkVisibleSides(tree_t *tree, int startbrush, int endbrush)
 +{
 +	int		i, j;
 +	mapbrush_t	*mb;
 +	int		numsides;
 +
 +	Log_Print("--- MarkVisibleSides ---\n");
 +
 +	// clear all the visible flags
 +	for (i=startbrush ; i<endbrush ; i++)
 +	{
 +		mb = &mapbrushes[i];
 +
 +		numsides = mb->numsides;
 +		for (j=0 ; j<numsides ; j++)
 +			mb->original_sides[j].flags &= ~SFL_VISIBLE;
 +	}
 +
 +	// set visible flags on the sides that are used by portals
 +	MarkVisibleSides_r (tree->headnode);
 +} //end of the function MarkVisibleSides
 +
 diff --git a/code/bspc/prtfile.c b/code/bspc/prtfile.c new file mode 100755 index 0000000..69834aa --- /dev/null +++ b/code/bspc/prtfile.c @@ -0,0 +1,287 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +
 +/*
 +==============================================================================
 +
 +PORTAL FILE GENERATION
 +
 +Save out name.prt for qvis to read
 +==============================================================================
 +*/
 +
 +
 +#define	PORTALFILE	"PRT1"
 +
 +FILE	*pf;
 +int		num_visclusters;				// clusters the player can be in
 +int		num_visportals;
 +
 +void WriteFloat2 (FILE *f, vec_t v)
 +{
 +	if ( fabs(v - Q_rint(v)) < 0.001 )
 +		fprintf (f,"%i ",(int)Q_rint(v));
 +	else
 +		fprintf (f,"%f ",v);
 +}
 +
 +/*
 +=================
 +WritePortalFile_r
 +=================
 +*/
 +void WritePortalFile_r (node_t *node)
 +{
 +	int			i, s;	
 +	portal_t	*p;
 +	winding_t	*w;
 +	vec3_t		normal;
 +	vec_t		dist;
 +
 +	// decision node
 +	if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
 +	{
 +		WritePortalFile_r (node->children[0]);
 +		WritePortalFile_r (node->children[1]);
 +		return;
 +	}
 +	
 +	if (node->contents & CONTENTS_SOLID)
 +		return;
 +
 +	for (p = node->portals ; p ; p=p->next[s])
 +	{
 +		w = p->winding;
 +		s = (p->nodes[1] == node);
 +		if (w && p->nodes[0] == node)
 +		{
 +			if (!Portal_VisFlood (p))
 +				continue;
 +		// write out to the file
 +		
 +		// sometimes planes get turned around when they are very near
 +		// the changeover point between different axis.  interpret the
 +		// plane the same way vis will, and flip the side orders if needed
 +			// FIXME: is this still relevent?
 +			WindingPlane (w, normal, &dist);
 +			if ( DotProduct (p->plane.normal, normal) < 0.99 )
 +			{	// backwards...
 +				fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
 +			}
 +			else
 +				fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
 +			for (i=0 ; i<w->numpoints ; i++)
 +			{
 +				fprintf (pf,"(");
 +				WriteFloat2 (pf, w->p[i][0]);
 +				WriteFloat2 (pf, w->p[i][1]);
 +				WriteFloat2 (pf, w->p[i][2]);
 +				fprintf (pf,") ");
 +			}
 +			fprintf (pf,"\n");
 +		}
 +	}
 +
 +}
 +
 +/*
 +================
 +FillLeafNumbers_r
 +
 +All of the leafs under node will have the same cluster
 +================
 +*/
 +void FillLeafNumbers_r (node_t *node, int num)
 +{
 +	if (node->planenum == PLANENUM_LEAF)
 +	{
 +		if (node->contents & CONTENTS_SOLID)
 +			node->cluster = -1;
 +		else
 +			node->cluster = num;
 +		return;
 +	}
 +	node->cluster = num;
 +	FillLeafNumbers_r (node->children[0], num);
 +	FillLeafNumbers_r (node->children[1], num);
 +}
 +
 +/*
 +================
 +NumberLeafs_r
 +================
 +*/
 +void NumberLeafs_r (node_t *node)
 +{
 +	portal_t	*p;
 +
 +	if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
 +	{	// decision node
 +		node->cluster = -99;
 +		NumberLeafs_r (node->children[0]);
 +		NumberLeafs_r (node->children[1]);
 +		return;
 +	}
 +	
 +	// either a leaf or a detail cluster
 +
 +	if ( node->contents & CONTENTS_SOLID )
 +	{	// solid block, viewpoint never inside
 +		node->cluster = -1;
 +		return;
 +	}
 +
 +	FillLeafNumbers_r (node, num_visclusters);
 +	num_visclusters++;
 +
 +	// count the portals
 +	for (p = node->portals ; p ; )
 +	{
 +		if (p->nodes[0] == node)		// only write out from first leaf
 +		{
 +			if (Portal_VisFlood (p))
 +				num_visportals++;
 +			p = p->next[0];
 +		}
 +		else
 +			p = p->next[1];		
 +	}
 +
 +}
 +
 +
 +/*
 +================
 +CreateVisPortals_r
 +================
 +*/
 +void CreateVisPortals_r (node_t *node)
 +{
 +	// stop as soon as we get to a detail_seperator, which
 +	// means that everything below is in a single cluster
 +	if (node->planenum == PLANENUM_LEAF || node->detail_seperator )
 +		return;
 +
 +	MakeNodePortal (node);
 +	SplitNodePortals (node);
 +
 +	CreateVisPortals_r (node->children[0]);
 +	CreateVisPortals_r (node->children[1]);
 +}
 +
 +/*
 +================
 +FinishVisPortals_r
 +================
 +*/
 +void FinishVisPortals2_r (node_t *node)
 +{
 +	if (node->planenum == PLANENUM_LEAF)
 +		return;
 +
 +	MakeNodePortal (node);
 +	SplitNodePortals (node);
 +
 +	FinishVisPortals2_r (node->children[0]);
 +	FinishVisPortals2_r (node->children[1]);
 +}
 +
 +void FinishVisPortals_r (node_t *node)
 +{
 +	if (node->planenum == PLANENUM_LEAF)
 +		return;
 +
 +	if (node->detail_seperator)
 +	{
 +		FinishVisPortals2_r (node);
 +		return;
 +	}
 +
 +	FinishVisPortals_r (node->children[0]);
 +	FinishVisPortals_r (node->children[1]);
 +}
 +
 +
 +int		clusterleaf;
 +void SaveClusters_r (node_t *node)
 +{
 +	if (node->planenum == PLANENUM_LEAF)
 +	{
 +		dleafs[clusterleaf++].cluster = node->cluster;
 +		return;
 +	}
 +	SaveClusters_r (node->children[0]);
 +	SaveClusters_r (node->children[1]);
 +}
 +
 +/*
 +================
 +WritePortalFile
 +================
 +*/
 +void WritePortalFile (tree_t *tree)
 +{
 +	char	filename[1024];
 +	node_t *headnode;
 +
 +	qprintf ("--- WritePortalFile ---\n");
 +
 +	headnode = tree->headnode;
 +	num_visclusters = 0;
 +	num_visportals = 0;
 +
 +	Tree_FreePortals_r (headnode);
 +
 +	MakeHeadnodePortals (tree);
 +
 +	CreateVisPortals_r (headnode);
 +
 +// set the cluster field in every leaf and count the total number of portals
 +
 +	NumberLeafs_r (headnode);
 +	
 +// write the file
 +	sprintf (filename, "%s.prt", source);
 +	printf ("writing %s\n", filename);
 +	pf = fopen (filename, "w");
 +	if (!pf)
 +		Error ("Error opening %s", filename);
 +		
 +	fprintf (pf, "%s\n", PORTALFILE);
 +	fprintf (pf, "%i\n", num_visclusters);
 +	fprintf (pf, "%i\n", num_visportals);
 +
 +	qprintf ("%5i visclusters\n", num_visclusters);
 +	qprintf ("%5i visportals\n", num_visportals);
 +
 +	WritePortalFile_r (headnode);
 +
 +	fclose (pf);
 +
 +	// we need to store the clusters out now because ordering
 +	// issues made us do this after writebsp...
 +	clusterleaf = 1;
 +	SaveClusters_r (headnode);
 +}
 +
 diff --git a/code/bspc/q2files.h b/code/bspc/q2files.h new file mode 100755 index 0000000..5ffea6e --- /dev/null +++ b/code/bspc/q2files.h @@ -0,0 +1,487 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//
 +// qfiles.h: quake file formats
 +// This file must be identical in the quake and utils directories
 +//
 +
 +/*
 +========================================================================
 +
 +The .pak files are just a linear collapse of a directory tree
 +
 +========================================================================
 +*/
 +
 +#define IDPAKHEADER		(('K'<<24)+('C'<<16)+('A'<<8)+'P')
 +
 +typedef struct
 +{
 +	char	name[56];
 +	int		filepos, filelen;
 +} dpackfile_t;
 +
 +typedef struct
 +{
 +	int		ident;		// == IDPAKHEADER
 +	int		dirofs;
 +	int		dirlen;
 +} dpackheader_t;
 +
 +#define	MAX_FILES_IN_PACK	4096
 +
 +
 +/*
 +========================================================================
 +
 +PCX files are used for as many images as possible
 +
 +========================================================================
 +*/
 +
 +typedef struct
 +{
 +    char	manufacturer;
 +    char	version;
 +    char	encoding;
 +    char	bits_per_pixel;
 +    unsigned short	xmin,ymin,xmax,ymax;
 +    unsigned short	hres,vres;
 +    unsigned char	palette[48];
 +    char	reserved;
 +    char	color_planes;
 +    unsigned short	bytes_per_line;
 +    unsigned short	palette_type;
 +    char	filler[58];
 +    unsigned char	data;			// unbounded
 +} pcx_t;
 +
 +
 +/*
 +========================================================================
 +
 +.MD2 triangle model file format
 +
 +========================================================================
 +*/
 +
 +#define IDALIASHEADER		(('2'<<24)+('P'<<16)+('D'<<8)+'I')
 +#define ALIAS_VERSION	8
 +
 +#define	MAX_TRIANGLES	4096
 +#define MAX_VERTS		2048
 +#define MAX_FRAMES		512
 +#define MAX_MD2SKINS	32
 +#define	MAX_SKINNAME	64
 +
 +typedef struct
 +{
 +	short	s;
 +	short	t;
 +} dstvert_t;
 +
 +typedef struct 
 +{
 +	short	index_xyz[3];
 +	short	index_st[3];
 +} dtriangle_t;
 +
 +typedef struct
 +{
 +	byte	v[3];			// scaled byte to fit in frame mins/maxs
 +	byte	lightnormalindex;
 +} dtrivertx_t;
 +
 +#define DTRIVERTX_V0   0
 +#define DTRIVERTX_V1   1
 +#define DTRIVERTX_V2   2
 +#define DTRIVERTX_LNI  3
 +#define DTRIVERTX_SIZE 4
 +
 +typedef struct
 +{
 +	float		scale[3];	// multiply byte verts by this
 +	float		translate[3];	// then add this
 +	char		name[16];	// frame name from grabbing
 +	dtrivertx_t	verts[1];	// variable sized
 +} daliasframe_t;
 +
 +
 +// the glcmd format:
 +// a positive integer starts a tristrip command, followed by that many
 +// vertex structures.
 +// a negative integer starts a trifan command, followed by -x vertexes
 +// a zero indicates the end of the command list.
 +// a vertex consists of a floating point s, a floating point t,
 +// and an integer vertex index.
 +
 +
 +typedef struct
 +{
 +	int			ident;
 +	int			version;
 +
 +	int			skinwidth;
 +	int			skinheight;
 +	int			framesize;		// byte size of each frame
 +
 +	int			num_skins;
 +	int			num_xyz;
 +	int			num_st;			// greater than num_xyz for seams
 +	int			num_tris;
 +	int			num_glcmds;		// dwords in strip/fan command list
 +	int			num_frames;
 +
 +	int			ofs_skins;		// each skin is a MAX_SKINNAME string
 +	int			ofs_st;			// byte offset from start for stverts
 +	int			ofs_tris;		// offset for dtriangles
 +	int			ofs_frames;		// offset for first frame
 +	int			ofs_glcmds;	
 +	int			ofs_end;		// end of file
 +
 +} dmdl_t;
 +
 +/*
 +========================================================================
 +
 +.SP2 sprite file format
 +
 +========================================================================
 +*/
 +
 +#define IDSPRITEHEADER	(('2'<<24)+('S'<<16)+('D'<<8)+'I')
 +		// little-endian "IDS2"
 +#define SPRITE_VERSION	2
 +
 +typedef struct
 +{
 +	int		width, height;
 +	int		origin_x, origin_y;		// raster coordinates inside pic
 +	char	name[MAX_SKINNAME];		// name of pcx file
 +} dsprframe_t;
 +
 +typedef struct {
 +	int			ident;
 +	int			version;
 +	int			numframes;
 +	dsprframe_t	frames[1];			// variable sized
 +} dsprite_t;
 +
 +/*
 +==============================================================================
 +
 +  .WAL texture file format
 +
 +==============================================================================
 +*/
 +
 +
 +#define	MIPLEVELS	4
 +typedef struct miptex_s
 +{
 +	char		name[32];
 +	unsigned	width, height;
 +	unsigned	offsets[MIPLEVELS];		// four mip maps stored
 +	char		animname[32];			// next frame in animation chain
 +	int			flags;
 +	int			contents;
 +	int			value;
 +} miptex_t;
 +
 +
 +
 +/*
 +==============================================================================
 +
 +  .BSP file format
 +
 +==============================================================================
 +*/
 +
 +#define IDBSPHEADER	(('P'<<24)+('S'<<16)+('B'<<8)+'I')
 +		// little-endian "IBSP"
 +
 +#define BSPVERSION	38
 +
 +
 +// upper design bounds
 +// leaffaces, leafbrushes, planes, and verts are still bounded by
 +// 16 bit short limits
 +#define	MAX_MAP_MODELS		1024
 +#define	MAX_MAP_BRUSHES		8192
 +#define	MAX_MAP_ENTITIES	2048
 +#define	MAX_MAP_ENTSTRING	0x40000
 +#define	MAX_MAP_TEXINFO		8192
 +
 +#define	MAX_MAP_AREAS		256
 +#define	MAX_MAP_AREAPORTALS	1024
 +#define	MAX_MAP_PLANES		65536
 +#define	MAX_MAP_NODES		65536
 +#define	MAX_MAP_BRUSHSIDES	65536
 +#define	MAX_MAP_LEAFS		65536
 +#define	MAX_MAP_VERTS		65536
 +#define	MAX_MAP_FACES		65536
 +#define	MAX_MAP_LEAFFACES	65536
 +#define	MAX_MAP_LEAFBRUSHES 65536
 +#define	MAX_MAP_PORTALS		65536
 +#define	MAX_MAP_EDGES		128000
 +#define	MAX_MAP_SURFEDGES	256000
 +#define	MAX_MAP_LIGHTING	0x320000
 +#define	MAX_MAP_VISIBILITY	0x280000
 +
 +// key / value pair sizes
 +
 +#define	MAX_KEY		32
 +#define	MAX_VALUE	1024
 +
 +//=============================================================================
 +
 +typedef struct
 +{
 +	int		fileofs, filelen;
 +} lump_t;
 +
 +#define	LUMP_ENTITIES		0
 +#define	LUMP_PLANES			1
 +#define	LUMP_VERTEXES		2
 +#define	LUMP_VISIBILITY		3
 +#define	LUMP_NODES			4
 +#define	LUMP_TEXINFO		5
 +#define	LUMP_FACES			6
 +#define	LUMP_LIGHTING		7
 +#define	LUMP_LEAFS			8
 +#define	LUMP_LEAFFACES		9
 +#define	LUMP_LEAFBRUSHES	10
 +#define	LUMP_EDGES			11
 +#define	LUMP_SURFEDGES		12
 +#define	LUMP_MODELS			13
 +#define	LUMP_BRUSHES		14
 +#define	LUMP_BRUSHSIDES		15
 +#define	LUMP_POP			16
 +#define	LUMP_AREAS			17
 +#define	LUMP_AREAPORTALS	18
 +#define	HEADER_LUMPS		19
 +
 +typedef struct
 +{
 +	int			ident;
 +	int			version;	
 +	lump_t		lumps[HEADER_LUMPS];
 +} dheader_t;
 +
 +typedef struct
 +{
 +	float		mins[3], maxs[3];
 +	float		origin[3];		// for sounds or lights
 +	int			headnode;
 +	int			firstface, numfaces;	// submodels just draw faces
 +										// without walking the bsp tree
 +} dmodel_t;
 +
 +
 +typedef struct
 +{
 +	float	point[3];
 +} dvertex_t;
 +
 +
 +// 0-2 are axial planes
 +#define	PLANE_X			0
 +#define	PLANE_Y			1
 +#define	PLANE_Z			2
 +
 +// 3-5 are non-axial planes snapped to the nearest
 +#define	PLANE_ANYX		3
 +#define	PLANE_ANYY		4
 +#define	PLANE_ANYZ		5
 +
 +// planes (x&~1) and (x&~1)+1 are allways opposites
 +
 +typedef struct
 +{
 +	float	normal[3];
 +	float	dist;
 +	int		type;		// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
 +} dplane_t;
 +
 +
 +// contents flags are seperate bits
 +// a given brush can contribute multiple content bits
 +// multiple brushes can be in a single leaf
 +
 +// these definitions also need to be in q_shared.h!
 +
 +// lower bits are stronger, and will eat weaker brushes completely
 +#define	CONTENTS_SOLID			1		// an eye is never valid in a solid
 +#define	CONTENTS_WINDOW			2		// translucent, but not watery
 +#define	CONTENTS_AUX			4
 +#define	CONTENTS_LAVA			8
 +#define	CONTENTS_SLIME			16
 +#define	CONTENTS_WATER			32
 +#define	CONTENTS_MIST			64
 +#define	LAST_VISIBLE_CONTENTS	64
 +
 +// remaining contents are non-visible, and don't eat brushes
 +
 +#define	CONTENTS_AREAPORTAL		0x8000
 +
 +#define	CONTENTS_PLAYERCLIP		0x10000
 +#define	CONTENTS_MONSTERCLIP	0x20000
 +
 +// currents can be added to any other contents, and may be mixed
 +#define	CONTENTS_CURRENT_0		0x40000
 +#define	CONTENTS_CURRENT_90		0x80000
 +#define	CONTENTS_CURRENT_180	0x100000
 +#define	CONTENTS_CURRENT_270	0x200000
 +#define	CONTENTS_CURRENT_UP		0x400000
 +#define	CONTENTS_CURRENT_DOWN	0x800000
 +
 +#define	CONTENTS_ORIGIN			0x1000000	// removed before bsping an entity
 +
 +#define	CONTENTS_MONSTER		0x2000000	// should never be on a brush, only in game
 +#define	CONTENTS_DEADMONSTER	0x4000000
 +#define	CONTENTS_DETAIL			0x8000000	// brushes to be added after vis leafs
 +//renamed because it's in conflict with the Q3A translucent contents
 +#define	CONTENTS_Q2TRANSLUCENT	0x10000000	// auto set if any surface has trans
 +#define	CONTENTS_LADDER			0x20000000
 +
 +
 +
 +#define	SURF_LIGHT		0x1		// value will hold the light strength
 +
 +#define	SURF_SLICK		0x2		// effects game physics
 +
 +#define	SURF_SKY		0x4		// don't draw, but add to skybox
 +#define	SURF_WARP		0x8		// turbulent water warp
 +#define	SURF_TRANS33	0x10
 +#define	SURF_TRANS66	0x20
 +#define	SURF_FLOWING	0x40	// scroll towards angle
 +#define	SURF_NODRAW		0x80	// don't bother referencing the texture
 +
 +#define	SURF_HINT		0x100	// make a primary bsp splitter
 +#define	SURF_SKIP		0x200	// completely ignore, allowing non-closed brushes
 +
 +
 +
 +typedef struct
 +{
 +	int			planenum;
 +	int			children[2];	// negative numbers are -(leafs+1), not nodes
 +	short		mins[3];		// for frustom culling
 +	short		maxs[3];
 +	unsigned short	firstface;
 +	unsigned short	numfaces;	// counting both sides
 +} dnode_t;
 +
 +
 +typedef struct texinfo_s
 +{
 +	float		vecs[2][4];		// [s/t][xyz offset]
 +	int			flags;			// miptex flags + overrides
 +	int			value;			// light emission, etc
 +	char		texture[32];	// texture name (textures/*.wal)
 +	int			nexttexinfo;	// for animations, -1 = end of chain
 +} texinfo_t;
 +
 +
 +// note that edge 0 is never used, because negative edge nums are used for
 +// counterclockwise use of the edge in a face
 +typedef struct
 +{
 +	unsigned short	v[2];		// vertex numbers
 +} dedge_t;
 +
 +#define	MAXLIGHTMAPS	4
 +typedef struct
 +{
 +	unsigned short	planenum;
 +	short		side;
 +
 +	int			firstedge;		// we must support > 64k edges
 +	short		numedges;	
 +	short		texinfo;
 +
 +// lighting info
 +	byte		styles[MAXLIGHTMAPS];
 +	int			lightofs;		// start of [numstyles*surfsize] samples
 +} dface_t;
 +
 +typedef struct
 +{
 +	int				contents;			// OR of all brushes (not needed?)
 +
 +	short			cluster;
 +	short			area;
 +
 +	short			mins[3];			// for frustum culling
 +	short			maxs[3];
 +
 +	unsigned short	firstleafface;
 +	unsigned short	numleaffaces;
 +
 +	unsigned short	firstleafbrush;
 +	unsigned short	numleafbrushes;
 +} dleaf_t;
 +
 +typedef struct
 +{
 +	unsigned short	planenum;		// facing out of the leaf
 +	short	texinfo;
 +} dbrushside_t;
 +
 +typedef struct
 +{
 +	int			firstside;
 +	int			numsides;
 +	int			contents;
 +} dbrush_t;
 +
 +#define	ANGLE_UP	-1
 +#define	ANGLE_DOWN	-2
 +
 +
 +// the visibility lump consists of a header with a count, then
 +// byte offsets for the PVS and PHS of each cluster, then the raw
 +// compressed bit vectors
 +#define	DVIS_PVS	0
 +#define	DVIS_PHS	1
 +typedef struct
 +{
 +	int			numclusters;
 +	int			bitofs[8][2];	// bitofs[numclusters][2]
 +} dvis_t;
 +
 +// each area has a list of portals that lead into other areas
 +// when portals are closed, other areas may not be visible or
 +// hearable even if the vis info says that it should be
 +typedef struct
 +{
 +	int		portalnum;
 +	int		otherarea;
 +} dareaportal_t;
 +
 +typedef struct
 +{
 +	int		numareaportals;
 +	int		firstareaportal;
 +} darea_t;
 diff --git a/code/bspc/q3files.h b/code/bspc/q3files.h new file mode 100755 index 0000000..b251cc0 --- /dev/null +++ b/code/bspc/q3files.h @@ -0,0 +1,374 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +#ifndef __QFILES_H__
 +#define __QFILES_H__
 +
 +//
 +// qfiles.h: quake file formats
 +// This file must be identical in the quake and utils directories
 +//
 +
 +// surface geometry should not exceed these limits
 +#define	SHADER_MAX_VERTEXES	1000
 +#define	SHADER_MAX_INDEXES	(6*SHADER_MAX_VERTEXES)
 +
 +
 +// the maximum size of game reletive pathnames
 +#define	MAX_QPATH		64
 +
 +
 +/*
 +========================================================================
 +
 +PCX files are used for 8 bit images
 +
 +========================================================================
 +* 
 +
 +typedef struct {
 +    char	manufacturer;
 +    char	version;
 +    char	encoding;
 +    char	bits_per_pixel;
 +    unsigned short	xmin,ymin,xmax,ymax;
 +    unsigned short	hres,vres;
 +    unsigned char	palette[48];
 +    char	reserved;
 +    char	color_planes;
 +    unsigned short	bytes_per_line;
 +    unsigned short	palette_type;
 +    char	filler[58];
 +    unsigned char	data;			// unbounded
 +} pcx_t;
 +
 +
 +/*
 +========================================================================
 +
 +TGA files are used for 24/32 bit images
 +
 +========================================================================
 +* 
 +
 +typedef struct _TargaHeader {
 +	unsigned char 	id_length, colormap_type, image_type;
 +	unsigned short	colormap_index, colormap_length;
 +	unsigned char	colormap_size;
 +	unsigned short	x_origin, y_origin, width, height;
 +	unsigned char	pixel_size, attributes;
 +} TargaHeader;
 +
 +
 +*/
 +
 +/*
 +========================================================================
 +
 +.MD3 triangle model file format
 +
 +========================================================================
 +*/
 +
 +#define MD3_IDENT			(('3'<<24)+('P'<<16)+('D'<<8)+'I')
 +#define MD3_VERSION			15
 +
 +// limits
 +#define MD3_MAX_LODS		4
 +#define	MD3_MAX_TRIANGLES	8192	// per surface
 +#define MD3_MAX_VERTS		4096	// per surface
 +#define MD3_MAX_SHADERS		256		// per surface
 +#define MD3_MAX_FRAMES		1024	// per model
 +#define	MD3_MAX_SURFACES	32		// per model
 +#define MD3_MAX_TAGS		16		// per frame
 +
 +// vertex scales
 +#define	MD3_XYZ_SCALE		(1.0/64)
 +
 +typedef struct md3Frame_s {
 +	vec3_t		bounds[2];
 +	vec3_t		localOrigin;
 +	float		radius;
 +	char		name[16];
 +} md3Frame_t;
 +
 +typedef struct md3Tag_s {
 +	char		name[MAX_QPATH];	// tag name
 +	vec3_t		origin;
 +	vec3_t		axis[3];
 +} md3Tag_t;
 +
 +/*
 +** md3Surface_t
 +**
 +** CHUNK			SIZE
 +** header			sizeof( md3Surface_t )
 +** shaders			sizeof( md3Shader_t ) * numShaders
 +** triangles[0]		sizeof( md3Triangle_t ) * numTriangles
 +** st				sizeof( md3St_t ) * numVerts
 +** XyzNormals		sizeof( md3XyzNormal_t ) * numVerts * numFrames
 +*/
 +
 +typedef struct {
 +	int		ident;				// 
 +
 +	char	name[MAX_QPATH];	// polyset name
 +
 +	int		flags;
 +	int		numFrames;			// all surfaces in a model should have the same
 +
 +	int		numShaders;			// all surfaces in a model should have the same
 +	int		numVerts;
 +
 +	int		numTriangles;
 +	int		ofsTriangles;
 +
 +	int		ofsShaders;			// offset from start of md3Surface_t
 +	int		ofsSt;				// texture coords are common for all frames
 +	int		ofsXyzNormals;		// numVerts * numFrames
 +
 +	int		ofsEnd;				// next surface follows
 +} md3Surface_t;
 +
 +typedef struct {
 +	char			name[MAX_QPATH];
 +	int				shaderIndex;	// for in-game use
 +} md3Shader_t;
 +
 +typedef struct {
 +	int			indexes[3];
 +} md3Triangle_t;
 +
 +typedef struct {
 +	float		st[2];
 +} md3St_t;
 +
 +typedef struct {
 +	short		xyz[3];
 +	short		normal;
 +} md3XyzNormal_t;
 +
 +typedef struct {
 +	int			ident;
 +	int			version;
 +
 +	char		name[MAX_QPATH];	// model name
 +
 +	int			flags;
 +
 +	int			numFrames;
 +	int			numTags;			
 +	int			numSurfaces;
 +
 +	int			numSkins;
 +
 +	int			ofsFrames;			// offset for first frame
 +	int			ofsTags;			// numFrames * numTags
 +	int			ofsSurfaces;		// first surface, others follow
 +
 +	int			ofsEnd;				// end of file
 +} md3Header_t;
 +
 +
 +
 +/*
 +==============================================================================
 +
 +  .BSP file format
 +
 +==============================================================================
 +*/
 +
 +
 +#define Q3_BSP_IDENT	(('P'<<24)+('S'<<16)+('B'<<8)+'I')
 +		// little-endian "IBSP"
 +
 +#define Q3_BSP_VERSION			46
 +
 +
 +// there shouldn't be any problem with increasing these values at the
 +// expense of more memory allocation in the utilities
 +#define	Q3_MAX_MAP_MODELS		0x400
 +#define	Q3_MAX_MAP_BRUSHES		0x8000
 +#define	Q3_MAX_MAP_ENTITIES	0x800
 +#define	Q3_MAX_MAP_ENTSTRING	0x10000
 +#define	Q3_MAX_MAP_SHADERS		0x400
 +
 +#define	Q3_MAX_MAP_AREAS		0x100	// MAX_MAP_AREA_BYTES in q_shared must match!
 +#define	Q3_MAX_MAP_FOGS		0x100
 +#define	Q3_MAX_MAP_PLANES		0x10000
 +#define	Q3_MAX_MAP_NODES		0x10000
 +#define	Q3_MAX_MAP_BRUSHSIDES	0x10000
 +#define	Q3_MAX_MAP_LEAFS		0x10000
 +#define	Q3_MAX_MAP_LEAFFACES	0x10000
 +#define	Q3_MAX_MAP_LEAFBRUSHES	0x10000
 +#define	Q3_MAX_MAP_PORTALS		0x10000
 +#define	Q3_MAX_MAP_LIGHTING	0x400000
 +#define	Q3_MAX_MAP_LIGHTGRID	0x400000
 +#define	Q3_MAX_MAP_VISIBILITY	0x200000
 +
 +#define	Q3_MAX_MAP_DRAW_SURFS	0x20000
 +#define	Q3_MAX_MAP_DRAW_VERTS	0x80000
 +#define	Q3_MAX_MAP_DRAW_INDEXES	0x80000
 +
 +
 +// key / value pair sizes in the entities lump
 +#define	Q3_MAX_KEY				32
 +#define	Q3_MAX_VALUE			1024
 +
 +// the editor uses these predefined yaw angles to orient entities up or down
 +#define	ANGLE_UP			-1
 +#define	ANGLE_DOWN			-2
 +
 +#define	LIGHTMAP_WIDTH		128
 +#define	LIGHTMAP_HEIGHT		128
 +
 +
 +//=============================================================================
 +
 +
 +typedef struct {
 +	int		fileofs, filelen;
 +} q3_lump_t;
 +
 +#define	Q3_LUMP_ENTITIES		0
 +#define	Q3_LUMP_SHADERS		1
 +#define	Q3_LUMP_PLANES			2
 +#define	Q3_LUMP_NODES			3
 +#define	Q3_LUMP_LEAFS			4
 +#define	Q3_LUMP_LEAFSURFACES	5
 +#define	Q3_LUMP_LEAFBRUSHES	6
 +#define	Q3_LUMP_MODELS			7
 +#define	Q3_LUMP_BRUSHES		8
 +#define	Q3_LUMP_BRUSHSIDES		9
 +#define	Q3_LUMP_DRAWVERTS		10
 +#define	Q3_LUMP_DRAWINDEXES	11
 +#define	Q3_LUMP_FOGS			12
 +#define	Q3_LUMP_SURFACES		13
 +#define	Q3_LUMP_LIGHTMAPS		14
 +#define	Q3_LUMP_LIGHTGRID		15
 +#define	Q3_LUMP_VISIBILITY		16
 +#define	Q3_HEADER_LUMPS		17
 +
 +typedef struct {
 +	int			ident;
 +	int			version;
 +
 +	q3_lump_t		lumps[Q3_HEADER_LUMPS];
 +} q3_dheader_t;
 +
 +typedef struct {
 +	float		mins[3], maxs[3];
 +	int			firstSurface, numSurfaces;
 +	int			firstBrush, numBrushes;
 +} q3_dmodel_t;
 +
 +typedef struct {
 +	char		shader[MAX_QPATH];
 +	int			surfaceFlags;
 +	int			contentFlags;
 +} q3_dshader_t;
 +
 +// planes (x&~1) and (x&~1)+1 are allways opposites
 +
 +typedef struct {
 +	float		normal[3];
 +	float		dist;
 +} q3_dplane_t;
 +
 +typedef struct {
 +	int			planeNum;
 +	int			children[2];	// negative numbers are -(leafs+1), not nodes
 +	int			mins[3];		// for frustom culling
 +	int			maxs[3];
 +} q3_dnode_t;
 +
 +typedef struct {
 +	int			cluster;			// -1 = opaque cluster (do I still store these?)
 +	int			area;
 +
 +	int			mins[3];			// for frustum culling
 +	int			maxs[3];
 +
 +	int			firstLeafSurface;
 +	int			numLeafSurfaces;
 +
 +	int			firstLeafBrush;
 +	int			numLeafBrushes;
 +} q3_dleaf_t;
 +
 +typedef struct {
 +	int			planeNum;			// positive plane side faces out of the leaf
 +	int			shaderNum;
 +} q3_dbrushside_t;
 +
 +typedef struct {
 +	int			firstSide;
 +	int			numSides;
 +	int			shaderNum;		// the shader that determines the contents flags
 +} q3_dbrush_t;
 +
 +typedef struct {
 +	char		shader[MAX_QPATH];
 +	int			brushNum;
 +	int			visibleSide;	// the brush side that ray tests need to clip against (-1 == none)
 +} q3_dfog_t;
 +
 +typedef struct {
 +	vec3_t		xyz;
 +	float		st[2];
 +	float		lightmap[2];
 +	vec3_t		normal;
 +	byte		color[4];
 +} q3_drawVert_t;
 +
 +typedef enum {
 +	MST_BAD,
 +	MST_PLANAR,
 +	MST_PATCH,
 +	MST_TRIANGLE_SOUP,
 +	MST_FLARE
 +} q3_mapSurfaceType_t;
 +
 +typedef struct {
 +	int			shaderNum;
 +	int			fogNum;
 +	int			surfaceType;
 +
 +	int			firstVert;
 +	int			numVerts;
 +
 +	int			firstIndex;
 +	int			numIndexes;
 +
 +	int			lightmapNum;
 +	int			lightmapX, lightmapY;
 +	int			lightmapWidth, lightmapHeight;
 +
 +	vec3_t		lightmapOrigin;
 +	vec3_t		lightmapVecs[3];	// for patches, [0] and [1] are lodbounds
 +
 +	int			patchWidth;
 +	int			patchHeight;
 +} q3_dsurface_t;
 +
 +
 +#endif
 diff --git a/code/bspc/qbsp.h b/code/bspc/qbsp.h new file mode 100755 index 0000000..7ebf9d4 --- /dev/null +++ b/code/bspc/qbsp.h @@ -0,0 +1,477 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +
 +#if defined(WIN32) || defined(_WIN32)
 +#include <io.h>
 +#endif
 +#include <malloc.h>
 +#include "l_cmd.h"
 +#include "l_math.h"
 +#include "l_poly.h"
 +#include "l_threads.h"
 +#include "../botlib/l_script.h"
 +#include "l_bsp_ent.h"
 +#include "q2files.h"
 +#include "l_mem.h"
 +#include "l_utils.h"
 +#include "l_log.h"
 +#include "l_qfiles.h"
 +
 +#define BSPC_VERSION		"2.1h"
 +
 +#define ME
 +#define DEBUG
 +#define NODELIST
 +#define SIN
 +
 +#define MAX_BRUSH_SIDES		128		//maximum number of sides per brush
 +#define CLIP_EPSILON		0.1
 +#define MAX_MAP_BOUNDS		65535
 +#define BOGUS_RANGE			(MAX_MAP_BOUNDS+128)	//somewhere outside the map
 +#define TEXINFO_NODE		-1		//side is allready on a node
 +#define PLANENUM_LEAF		-1		//used for leaf nodes
 +#define MAXEDGES			20		//maximum number of face edges
 +#define MAX_NODE_BRUSHES	8		//maximum brushes in a node
 +//side flags
 +#define SFL_TESTED			1
 +#define SFL_VISIBLE			2
 +#define SFL_BEVEL			4
 +#define SFL_TEXTURED		8
 +#define SFL_CURVE			16
 +
 +//map plane
 +typedef struct plane_s
 +{
 +	vec3_t normal;
 +	vec_t dist;
 +	int type;
 +	int signbits;
 +	struct plane_s	*hash_chain;
 +} plane_t;
 +//brush texture
 +typedef struct
 +{
 +	vec_t	shift[2];
 +	vec_t	rotate;
 +	vec_t	scale[2];
 +	char	name[32];
 +	int		flags;
 +	int		value;
 +} brush_texture_t;
 +//brush side
 +typedef struct side_s
 +{
 +	int				planenum;	// map plane this side is in
 +	int				texinfo;		// texture reference
 +	winding_t		*winding;	// winding of this side
 +	struct side_s	*original;	// bspbrush_t sides will reference the mapbrush_t sides
 +   int				lightinfo;	// for SIN only
 +	int				contents;	// from miptex
 +	int				surf;			// from miptex
 +	unsigned short flags;		// side flags
 +} side_t;		//sizeof(side_t) = 36
 +//map brush
 +typedef struct mapbrush_s
 +{
 +	int		entitynum;
 +	int		brushnum;
 +
 +	int		contents;
 +#ifdef ME
 +	int		expansionbbox;			//bbox used for expansion of the brush
 +	int		leafnum;
 +	int		modelnum;
 +#endif
 +
 +	vec3_t	mins, maxs;
 +
 +	int		numsides;
 +	side_t	*original_sides;
 +} mapbrush_t;
 +//bsp face
 +typedef struct face_s
 +{
 +	struct face_s		*next;		// on node
 +
 +	// the chain of faces off of a node can be merged or split,
 +	// but each face_t along the way will remain in the chain
 +	// until the entire tree is freed
 +	struct face_s		*merged;	// if set, this face isn't valid anymore
 +	struct face_s		*split[2];	// if set, this face isn't valid anymore
 +
 +	struct portal_s	*portal;
 +	int					texinfo;
 +#ifdef SIN
 +   int					lightinfo;
 +#endif
 +	int					planenum;
 +	int					contents;	// faces in different contents can't merge
 +	int					outputnumber;
 +	winding_t			*w;
 +	int					numpoints;
 +	qboolean				badstartvert;	// tjunctions cannot be fixed without a midpoint vertex
 +	int					vertexnums[MAXEDGES];
 +} face_t;
 +//bsp brush
 +typedef struct bspbrush_s
 +{
 +	struct		bspbrush_s	*next;
 +	vec3_t		mins, maxs;
 +	int			side, testside;		// side of node during construction
 +	mapbrush_t	*original;
 +	int			numsides;
 +	side_t		sides[6];			// variably sized
 +} bspbrush_t;	//sizeof(bspbrush_t) = 44 + numsides * sizeof(side_t)
 +//bsp node
 +typedef struct node_s
 +{
 +	//both leafs and nodes
 +	int				planenum;	// -1 = leaf node
 +	struct node_s	*parent;
 +	vec3_t			mins, maxs;	// valid after portalization
 +	bspbrush_t		*volume;		// one for each leaf/node
 +
 +	// nodes only
 +	qboolean			detail_seperator;	// a detail brush caused the split
 +	side_t			*side;		// the side that created the node
 +	struct node_s	*children[2];
 +	face_t			*faces;
 +
 +	// leafs only
 +	bspbrush_t		*brushlist;	// fragments of all brushes in this leaf
 +	int				contents;	// OR of all brush contents
 +	int				occupied;	// 1 or greater can reach entity
 +	entity_t			*occupant;	// for leak file testing
 +	int				cluster;		// for portalfile writing
 +	int				area;			// for areaportals
 +	struct portal_s *portals;	// also on nodes during construction
 +#ifdef NODELIST
 +	struct node_s *next;			//next node in the nodelist
 +#endif
 +#ifdef ME
 +	int expansionbboxes;			//OR of all bboxes used for expansion of the brushes
 +	int modelnum;
 +#endif
 +} node_t;		//sizeof(node_t) = 80 bytes
 +//bsp portal
 +typedef struct portal_s
 +{
 +	plane_t plane;
 +	node_t *onnode;					// NULL = outside box
 +	node_t *nodes[2];				// [0] = front side of plane
 +	struct portal_s *next[2];
 +	winding_t *winding;
 +
 +	qboolean	sidefound;			// false if ->side hasn't been checked
 +	side_t *side;					// NULL = non-visible
 +	face_t *face[2];				// output face in bsp file
 +#ifdef ME
 +	struct tmp_face_s *tmpface;		//pointer to the tmpface created for this portal
 +	int planenum;					//number of the map plane used by the portal
 +#endif
 +} portal_t;
 +//bsp tree
 +typedef struct
 +{
 +	node_t		*headnode;
 +	node_t		outside_node;
 +	vec3_t		mins, maxs;
 +} tree_t;
 +
 +//=============================================================================
 +// bspc.c
 +//=============================================================================
 +
 +extern	qboolean noprune;
 +extern	qboolean nodetail;
 +extern	qboolean fulldetail;
 +extern	qboolean nomerge;
 +extern	qboolean nosubdiv;
 +extern	qboolean nowater;
 +extern	qboolean noweld;
 +extern	qboolean noshare;
 +extern	qboolean notjunc;
 +extern	qboolean onlyents;
 +#ifdef ME
 +extern	qboolean nocsg;
 +extern	qboolean create_aas;
 +extern	qboolean freetree;
 +extern	qboolean lessbrushes;
 +extern	qboolean nobrushmerge;
 +extern	qboolean cancelconversion;
 +extern	qboolean noliquids;
 +extern	qboolean capsule_collision;
 +#endif //ME
 +
 +extern	float subdivide_size;
 +extern	vec_t microvolume;
 +
 +extern	char outbase[32];
 +extern	char source[1024];
 +
 +//=============================================================================
 +// map.c
 +//=============================================================================
 +
 +#define MAX_MAPFILE_PLANES			256000
 +#define MAX_MAPFILE_BRUSHES			65535
 +#define MAX_MAPFILE_BRUSHSIDES		(MAX_MAPFILE_BRUSHES*8)
 +#define MAX_MAPFILE_TEXINFO			8192
 +
 +extern	int			entity_num;
 +
 +extern	plane_t		mapplanes[MAX_MAPFILE_PLANES];
 +extern	int			nummapplanes;
 +extern	int			mapplaneusers[MAX_MAPFILE_PLANES];
 +
 +extern	int			nummapbrushes;
 +extern	mapbrush_t	mapbrushes[MAX_MAPFILE_BRUSHES];
 +
 +extern	vec3_t		map_mins, map_maxs;
 +
 +extern	int			nummapbrushsides;
 +extern	side_t		brushsides[MAX_MAPFILE_BRUSHSIDES];
 +extern	brush_texture_t	side_brushtextures[MAX_MAPFILE_BRUSHSIDES];
 +
 +#ifdef ME
 +
 +typedef struct
 +{
 +	float	vecs[2][4];		// [s/t][xyz offset]
 +	int		flags;			// miptex flags + overrides
 +	int		value;
 +	char	texture[64];	// texture name (textures/*.wal)
 +	int		nexttexinfo;	// for animations, -1 = end of chain
 +} map_texinfo_t;
 +
 +extern	map_texinfo_t		map_texinfo[MAX_MAPFILE_TEXINFO];
 +extern	int					map_numtexinfo;
 +#define NODESTACKSIZE		1024
 +
 +#define MAPTYPE_QUAKE1		1
 +#define MAPTYPE_QUAKE2		2
 +#define MAPTYPE_QUAKE3		3
 +#define MAPTYPE_HALFLIFE	4
 +#define MAPTYPE_SIN			5
 +
 +extern	int nodestack[NODESTACKSIZE];
 +extern	int *nodestackptr;
 +extern	int nodestacksize;
 +extern	int brushmodelnumbers[MAX_MAPFILE_BRUSHES];
 +extern	int dbrushleafnums[MAX_MAPFILE_BRUSHES];
 +extern	int dplanes2mapplanes[MAX_MAPFILE_PLANES];
 +
 +extern	int loadedmaptype;
 +#endif //ME
 +
 +extern	int c_boxbevels;
 +extern	int c_edgebevels;
 +extern	int c_areaportals;
 +extern	int c_clipbrushes;
 +extern	int c_squattbrushes;
 +
 +//finds a float plane for the given normal and distance
 +int FindFloatPlane(vec3_t normal, vec_t dist);
 +//returns the plane type for the given normal
 +int PlaneTypeForNormal(vec3_t normal);
 +//returns the plane defined by the three given points
 +int PlaneFromPoints(int *p0, int *p1, int *p2);
 +//add bevels to the map brush
 +void AddBrushBevels(mapbrush_t *b);
 +//makes brush side windings for the brush
 +qboolean MakeBrushWindings(mapbrush_t *ob);
 +//marks brush bevels of the brush as bevel
 +void MarkBrushBevels(mapbrush_t *brush);
 +//returns true if the map brush already exists
 +int BrushExists(mapbrush_t *brush);
 +//loads a map from a bsp file
 +int LoadMapFromBSP(struct quakefile_s *qf);
 +//resets map loading
 +void ResetMapLoading(void);
 +//print some map info
 +void PrintMapInfo(void);
 +//writes a map file (type depending on loaded map type)
 +void WriteMapFile(char *filename);
 +
 +//=============================================================================
 +// map_q2.c
 +//=============================================================================
 +
 +void Q2_ResetMapLoading(void);
 +//loads a Quake2 map file
 +void Q2_LoadMapFile(char *filename);
 +//loads a map from a Quake2 bsp file
 +void Q2_LoadMapFromBSP(char *filename, int offset, int length);
 +
 +//=============================================================================
 +// map_q1.c
 +//=============================================================================
 +
 +void Q1_ResetMapLoading(void);
 +//loads a Quake2 map file
 +void Q1_LoadMapFile(char *filename);
 +//loads a map from a Quake1 bsp file
 +void Q1_LoadMapFromBSP(char *filename, int offset, int length);
 +
 +//=============================================================================
 +// map_q3.c
 +//=============================================================================
 +void Q3_ResetMapLoading(void);
 +//loads a map from a Quake3 bsp file
 +void Q3_LoadMapFromBSP(struct quakefile_s *qf);
 +
 +//=============================================================================
 +// map_sin.c
 +//=============================================================================
 +
 +void Sin_ResetMapLoading(void);
 +//loads a Sin map file
 +void Sin_LoadMapFile(char *filename);
 +//loads a map from a Sin bsp file
 +void Sin_LoadMapFromBSP(char *filename, int offset, int length);
 +
 +//=============================================================================
 +// map_hl.c
 +//=============================================================================
 +
 +void HL_ResetMapLoading(void);
 +//loads a Half-Life map file
 +void HL_LoadMapFile(char *filename);
 +//loads a map from a Half-Life bsp file
 +void HL_LoadMapFromBSP(char *filename, int offset, int length);
 +
 +//=============================================================================
 +// textures.c
 +//=============================================================================
 +
 +typedef struct
 +{
 +	char	name[64];
 +	int		flags;
 +	int		value;
 +	int		contents;
 +	char	animname[64];
 +} textureref_t;
 +
 +#define	MAX_MAP_TEXTURES	1024
 +
 +extern	textureref_t	textureref[MAX_MAP_TEXTURES];
 +
 +int FindMiptex(char *name);
 +int TexinfoForBrushTexture(plane_t *plane, brush_texture_t *bt, vec3_t origin);
 +void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv);
 +
 +//=============================================================================
 +// csg
 +//=============================================================================
 +
 +bspbrush_t *MakeBspBrushList(int startbrush, int endbrush, vec3_t clipmins, vec3_t clipmaxs);
 +bspbrush_t *ChopBrushes(bspbrush_t *head);
 +bspbrush_t *InitialBrushList(bspbrush_t *list);
 +bspbrush_t *OptimizedBrushList(bspbrush_t *list);
 +void WriteBrushMap(char *name, bspbrush_t *list);
 +void CheckBSPBrush(bspbrush_t *brush);
 +void BSPBrushWindings(bspbrush_t *brush);
 +bspbrush_t *TryMergeBrushes(bspbrush_t *brush1, bspbrush_t *brush2);
 +tree_t *ProcessWorldBrushes(int brush_start, int brush_end);
 +
 +//=============================================================================
 +// brushbsp
 +//=============================================================================
 +
 +#define	PSIDE_FRONT			1
 +#define	PSIDE_BACK			2
 +#define	PSIDE_BOTH			(PSIDE_FRONT|PSIDE_BACK)
 +#define	PSIDE_FACING		4
 +
 +void WriteBrushList(char *name, bspbrush_t *brush, qboolean onlyvis);
 +bspbrush_t *CopyBrush(bspbrush_t *brush);
 +void SplitBrush(bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back);
 +node_t *AllocNode(void);
 +bspbrush_t *AllocBrush(int numsides);
 +int CountBrushList(bspbrush_t *brushes);
 +void FreeBrush(bspbrush_t *brushes);
 +vec_t BrushVolume(bspbrush_t *brush);
 +void BoundBrush(bspbrush_t *brush);
 +void FreeBrushList(bspbrush_t *brushes);
 +tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
 +bspbrush_t *BrushFromBounds(vec3_t mins, vec3_t maxs);
 +int BrushMostlyOnSide(bspbrush_t *brush, plane_t *plane);
 +qboolean WindingIsHuge(winding_t *w);
 +qboolean WindingIsTiny(winding_t *w);
 +void ResetBrushBSP(void);
 +
 +//=============================================================================
 +// portals.c
 +//=============================================================================
 +
 +int VisibleContents (int contents);
 +void MakeHeadnodePortals (tree_t *tree);
 +void MakeNodePortal (node_t *node);
 +void SplitNodePortals (node_t *node);
 +qboolean Portal_VisFlood (portal_t *p);
 +qboolean FloodEntities (tree_t *tree);
 +void FillOutside (node_t *headnode);
 +void FloodAreas (tree_t *tree);
 +void MarkVisibleSides (tree_t *tree, int start, int end);
 +void FreePortal (portal_t *p);
 +void EmitAreaPortals (node_t *headnode);
 +void MakeTreePortals (tree_t *tree);
 +
 +//=============================================================================
 +// glfile.c
 +//=============================================================================
 +
 +void OutputWinding(winding_t *w, FILE *glview);
 +void WriteGLView(tree_t *tree, char *source);
 +
 +//=============================================================================
 +// gldraw.c
 +//=============================================================================
 +
 +extern vec3_t draw_mins, draw_maxs;
 +extern qboolean drawflag;
 +
 +void Draw_ClearWindow (void);
 +void DrawWinding (winding_t *w);
 +void GLS_BeginScene (void);
 +void GLS_Winding (winding_t *w, int code);
 +void GLS_EndScene (void);
 +
 +//=============================================================================
 +// leakfile.c
 +//=============================================================================
 +
 +void LeakFile (tree_t *tree);
 +
 +//=============================================================================
 +// tree.c
 +//=============================================================================
 +
 +tree_t *Tree_Alloc(void);
 +void Tree_Free(tree_t *tree);
 +void Tree_Free_r(node_t *node);
 +void Tree_Print_r(node_t *node, int depth);
 +void Tree_FreePortals_r(node_t *node);
 +void Tree_PruneNodes_r(node_t *node);
 +void Tree_PruneNodes(node_t *node);
 diff --git a/code/bspc/qfiles.h b/code/bspc/qfiles.h new file mode 100755 index 0000000..5ffea6e --- /dev/null +++ b/code/bspc/qfiles.h @@ -0,0 +1,487 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +//
 +// qfiles.h: quake file formats
 +// This file must be identical in the quake and utils directories
 +//
 +
 +/*
 +========================================================================
 +
 +The .pak files are just a linear collapse of a directory tree
 +
 +========================================================================
 +*/
 +
 +#define IDPAKHEADER		(('K'<<24)+('C'<<16)+('A'<<8)+'P')
 +
 +typedef struct
 +{
 +	char	name[56];
 +	int		filepos, filelen;
 +} dpackfile_t;
 +
 +typedef struct
 +{
 +	int		ident;		// == IDPAKHEADER
 +	int		dirofs;
 +	int		dirlen;
 +} dpackheader_t;
 +
 +#define	MAX_FILES_IN_PACK	4096
 +
 +
 +/*
 +========================================================================
 +
 +PCX files are used for as many images as possible
 +
 +========================================================================
 +*/
 +
 +typedef struct
 +{
 +    char	manufacturer;
 +    char	version;
 +    char	encoding;
 +    char	bits_per_pixel;
 +    unsigned short	xmin,ymin,xmax,ymax;
 +    unsigned short	hres,vres;
 +    unsigned char	palette[48];
 +    char	reserved;
 +    char	color_planes;
 +    unsigned short	bytes_per_line;
 +    unsigned short	palette_type;
 +    char	filler[58];
 +    unsigned char	data;			// unbounded
 +} pcx_t;
 +
 +
 +/*
 +========================================================================
 +
 +.MD2 triangle model file format
 +
 +========================================================================
 +*/
 +
 +#define IDALIASHEADER		(('2'<<24)+('P'<<16)+('D'<<8)+'I')
 +#define ALIAS_VERSION	8
 +
 +#define	MAX_TRIANGLES	4096
 +#define MAX_VERTS		2048
 +#define MAX_FRAMES		512
 +#define MAX_MD2SKINS	32
 +#define	MAX_SKINNAME	64
 +
 +typedef struct
 +{
 +	short	s;
 +	short	t;
 +} dstvert_t;
 +
 +typedef struct 
 +{
 +	short	index_xyz[3];
 +	short	index_st[3];
 +} dtriangle_t;
 +
 +typedef struct
 +{
 +	byte	v[3];			// scaled byte to fit in frame mins/maxs
 +	byte	lightnormalindex;
 +} dtrivertx_t;
 +
 +#define DTRIVERTX_V0   0
 +#define DTRIVERTX_V1   1
 +#define DTRIVERTX_V2   2
 +#define DTRIVERTX_LNI  3
 +#define DTRIVERTX_SIZE 4
 +
 +typedef struct
 +{
 +	float		scale[3];	// multiply byte verts by this
 +	float		translate[3];	// then add this
 +	char		name[16];	// frame name from grabbing
 +	dtrivertx_t	verts[1];	// variable sized
 +} daliasframe_t;
 +
 +
 +// the glcmd format:
 +// a positive integer starts a tristrip command, followed by that many
 +// vertex structures.
 +// a negative integer starts a trifan command, followed by -x vertexes
 +// a zero indicates the end of the command list.
 +// a vertex consists of a floating point s, a floating point t,
 +// and an integer vertex index.
 +
 +
 +typedef struct
 +{
 +	int			ident;
 +	int			version;
 +
 +	int			skinwidth;
 +	int			skinheight;
 +	int			framesize;		// byte size of each frame
 +
 +	int			num_skins;
 +	int			num_xyz;
 +	int			num_st;			// greater than num_xyz for seams
 +	int			num_tris;
 +	int			num_glcmds;		// dwords in strip/fan command list
 +	int			num_frames;
 +
 +	int			ofs_skins;		// each skin is a MAX_SKINNAME string
 +	int			ofs_st;			// byte offset from start for stverts
 +	int			ofs_tris;		// offset for dtriangles
 +	int			ofs_frames;		// offset for first frame
 +	int			ofs_glcmds;	
 +	int			ofs_end;		// end of file
 +
 +} dmdl_t;
 +
 +/*
 +========================================================================
 +
 +.SP2 sprite file format
 +
 +========================================================================
 +*/
 +
 +#define IDSPRITEHEADER	(('2'<<24)+('S'<<16)+('D'<<8)+'I')
 +		// little-endian "IDS2"
 +#define SPRITE_VERSION	2
 +
 +typedef struct
 +{
 +	int		width, height;
 +	int		origin_x, origin_y;		// raster coordinates inside pic
 +	char	name[MAX_SKINNAME];		// name of pcx file
 +} dsprframe_t;
 +
 +typedef struct {
 +	int			ident;
 +	int			version;
 +	int			numframes;
 +	dsprframe_t	frames[1];			// variable sized
 +} dsprite_t;
 +
 +/*
 +==============================================================================
 +
 +  .WAL texture file format
 +
 +==============================================================================
 +*/
 +
 +
 +#define	MIPLEVELS	4
 +typedef struct miptex_s
 +{
 +	char		name[32];
 +	unsigned	width, height;
 +	unsigned	offsets[MIPLEVELS];		// four mip maps stored
 +	char		animname[32];			// next frame in animation chain
 +	int			flags;
 +	int			contents;
 +	int			value;
 +} miptex_t;
 +
 +
 +
 +/*
 +==============================================================================
 +
 +  .BSP file format
 +
 +==============================================================================
 +*/
 +
 +#define IDBSPHEADER	(('P'<<24)+('S'<<16)+('B'<<8)+'I')
 +		// little-endian "IBSP"
 +
 +#define BSPVERSION	38
 +
 +
 +// upper design bounds
 +// leaffaces, leafbrushes, planes, and verts are still bounded by
 +// 16 bit short limits
 +#define	MAX_MAP_MODELS		1024
 +#define	MAX_MAP_BRUSHES		8192
 +#define	MAX_MAP_ENTITIES	2048
 +#define	MAX_MAP_ENTSTRING	0x40000
 +#define	MAX_MAP_TEXINFO		8192
 +
 +#define	MAX_MAP_AREAS		256
 +#define	MAX_MAP_AREAPORTALS	1024
 +#define	MAX_MAP_PLANES		65536
 +#define	MAX_MAP_NODES		65536
 +#define	MAX_MAP_BRUSHSIDES	65536
 +#define	MAX_MAP_LEAFS		65536
 +#define	MAX_MAP_VERTS		65536
 +#define	MAX_MAP_FACES		65536
 +#define	MAX_MAP_LEAFFACES	65536
 +#define	MAX_MAP_LEAFBRUSHES 65536
 +#define	MAX_MAP_PORTALS		65536
 +#define	MAX_MAP_EDGES		128000
 +#define	MAX_MAP_SURFEDGES	256000
 +#define	MAX_MAP_LIGHTING	0x320000
 +#define	MAX_MAP_VISIBILITY	0x280000
 +
 +// key / value pair sizes
 +
 +#define	MAX_KEY		32
 +#define	MAX_VALUE	1024
 +
 +//=============================================================================
 +
 +typedef struct
 +{
 +	int		fileofs, filelen;
 +} lump_t;
 +
 +#define	LUMP_ENTITIES		0
 +#define	LUMP_PLANES			1
 +#define	LUMP_VERTEXES		2
 +#define	LUMP_VISIBILITY		3
 +#define	LUMP_NODES			4
 +#define	LUMP_TEXINFO		5
 +#define	LUMP_FACES			6
 +#define	LUMP_LIGHTING		7
 +#define	LUMP_LEAFS			8
 +#define	LUMP_LEAFFACES		9
 +#define	LUMP_LEAFBRUSHES	10
 +#define	LUMP_EDGES			11
 +#define	LUMP_SURFEDGES		12
 +#define	LUMP_MODELS			13
 +#define	LUMP_BRUSHES		14
 +#define	LUMP_BRUSHSIDES		15
 +#define	LUMP_POP			16
 +#define	LUMP_AREAS			17
 +#define	LUMP_AREAPORTALS	18
 +#define	HEADER_LUMPS		19
 +
 +typedef struct
 +{
 +	int			ident;
 +	int			version;	
 +	lump_t		lumps[HEADER_LUMPS];
 +} dheader_t;
 +
 +typedef struct
 +{
 +	float		mins[3], maxs[3];
 +	float		origin[3];		// for sounds or lights
 +	int			headnode;
 +	int			firstface, numfaces;	// submodels just draw faces
 +										// without walking the bsp tree
 +} dmodel_t;
 +
 +
 +typedef struct
 +{
 +	float	point[3];
 +} dvertex_t;
 +
 +
 +// 0-2 are axial planes
 +#define	PLANE_X			0
 +#define	PLANE_Y			1
 +#define	PLANE_Z			2
 +
 +// 3-5 are non-axial planes snapped to the nearest
 +#define	PLANE_ANYX		3
 +#define	PLANE_ANYY		4
 +#define	PLANE_ANYZ		5
 +
 +// planes (x&~1) and (x&~1)+1 are allways opposites
 +
 +typedef struct
 +{
 +	float	normal[3];
 +	float	dist;
 +	int		type;		// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
 +} dplane_t;
 +
 +
 +// contents flags are seperate bits
 +// a given brush can contribute multiple content bits
 +// multiple brushes can be in a single leaf
 +
 +// these definitions also need to be in q_shared.h!
 +
 +// lower bits are stronger, and will eat weaker brushes completely
 +#define	CONTENTS_SOLID			1		// an eye is never valid in a solid
 +#define	CONTENTS_WINDOW			2		// translucent, but not watery
 +#define	CONTENTS_AUX			4
 +#define	CONTENTS_LAVA			8
 +#define	CONTENTS_SLIME			16
 +#define	CONTENTS_WATER			32
 +#define	CONTENTS_MIST			64
 +#define	LAST_VISIBLE_CONTENTS	64
 +
 +// remaining contents are non-visible, and don't eat brushes
 +
 +#define	CONTENTS_AREAPORTAL		0x8000
 +
 +#define	CONTENTS_PLAYERCLIP		0x10000
 +#define	CONTENTS_MONSTERCLIP	0x20000
 +
 +// currents can be added to any other contents, and may be mixed
 +#define	CONTENTS_CURRENT_0		0x40000
 +#define	CONTENTS_CURRENT_90		0x80000
 +#define	CONTENTS_CURRENT_180	0x100000
 +#define	CONTENTS_CURRENT_270	0x200000
 +#define	CONTENTS_CURRENT_UP		0x400000
 +#define	CONTENTS_CURRENT_DOWN	0x800000
 +
 +#define	CONTENTS_ORIGIN			0x1000000	// removed before bsping an entity
 +
 +#define	CONTENTS_MONSTER		0x2000000	// should never be on a brush, only in game
 +#define	CONTENTS_DEADMONSTER	0x4000000
 +#define	CONTENTS_DETAIL			0x8000000	// brushes to be added after vis leafs
 +//renamed because it's in conflict with the Q3A translucent contents
 +#define	CONTENTS_Q2TRANSLUCENT	0x10000000	// auto set if any surface has trans
 +#define	CONTENTS_LADDER			0x20000000
 +
 +
 +
 +#define	SURF_LIGHT		0x1		// value will hold the light strength
 +
 +#define	SURF_SLICK		0x2		// effects game physics
 +
 +#define	SURF_SKY		0x4		// don't draw, but add to skybox
 +#define	SURF_WARP		0x8		// turbulent water warp
 +#define	SURF_TRANS33	0x10
 +#define	SURF_TRANS66	0x20
 +#define	SURF_FLOWING	0x40	// scroll towards angle
 +#define	SURF_NODRAW		0x80	// don't bother referencing the texture
 +
 +#define	SURF_HINT		0x100	// make a primary bsp splitter
 +#define	SURF_SKIP		0x200	// completely ignore, allowing non-closed brushes
 +
 +
 +
 +typedef struct
 +{
 +	int			planenum;
 +	int			children[2];	// negative numbers are -(leafs+1), not nodes
 +	short		mins[3];		// for frustom culling
 +	short		maxs[3];
 +	unsigned short	firstface;
 +	unsigned short	numfaces;	// counting both sides
 +} dnode_t;
 +
 +
 +typedef struct texinfo_s
 +{
 +	float		vecs[2][4];		// [s/t][xyz offset]
 +	int			flags;			// miptex flags + overrides
 +	int			value;			// light emission, etc
 +	char		texture[32];	// texture name (textures/*.wal)
 +	int			nexttexinfo;	// for animations, -1 = end of chain
 +} texinfo_t;
 +
 +
 +// note that edge 0 is never used, because negative edge nums are used for
 +// counterclockwise use of the edge in a face
 +typedef struct
 +{
 +	unsigned short	v[2];		// vertex numbers
 +} dedge_t;
 +
 +#define	MAXLIGHTMAPS	4
 +typedef struct
 +{
 +	unsigned short	planenum;
 +	short		side;
 +
 +	int			firstedge;		// we must support > 64k edges
 +	short		numedges;	
 +	short		texinfo;
 +
 +// lighting info
 +	byte		styles[MAXLIGHTMAPS];
 +	int			lightofs;		// start of [numstyles*surfsize] samples
 +} dface_t;
 +
 +typedef struct
 +{
 +	int				contents;			// OR of all brushes (not needed?)
 +
 +	short			cluster;
 +	short			area;
 +
 +	short			mins[3];			// for frustum culling
 +	short			maxs[3];
 +
 +	unsigned short	firstleafface;
 +	unsigned short	numleaffaces;
 +
 +	unsigned short	firstleafbrush;
 +	unsigned short	numleafbrushes;
 +} dleaf_t;
 +
 +typedef struct
 +{
 +	unsigned short	planenum;		// facing out of the leaf
 +	short	texinfo;
 +} dbrushside_t;
 +
 +typedef struct
 +{
 +	int			firstside;
 +	int			numsides;
 +	int			contents;
 +} dbrush_t;
 +
 +#define	ANGLE_UP	-1
 +#define	ANGLE_DOWN	-2
 +
 +
 +// the visibility lump consists of a header with a count, then
 +// byte offsets for the PVS and PHS of each cluster, then the raw
 +// compressed bit vectors
 +#define	DVIS_PVS	0
 +#define	DVIS_PHS	1
 +typedef struct
 +{
 +	int			numclusters;
 +	int			bitofs[8][2];	// bitofs[numclusters][2]
 +} dvis_t;
 +
 +// each area has a list of portals that lead into other areas
 +// when portals are closed, other areas may not be visible or
 +// hearable even if the vis info says that it should be
 +typedef struct
 +{
 +	int		portalnum;
 +	int		otherarea;
 +} dareaportal_t;
 +
 +typedef struct
 +{
 +	int		numareaportals;
 +	int		firstareaportal;
 +} darea_t;
 diff --git a/code/bspc/sinfiles.h b/code/bspc/sinfiles.h new file mode 100755 index 0000000..31091c5 --- /dev/null +++ b/code/bspc/sinfiles.h @@ -0,0 +1,365 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +/*
 +==============================================================================
 +
 +  .BSP file format
 +
 +==============================================================================
 +*/
 +
 +#define SIN
 +
 +#define SINBSPVERSION	41
 +
 +// upper design bounds
 +// leaffaces, leafbrushes, planes, and verts are still bounded by
 +// 16 bit short limits
 +#define	SIN_MAX_MAP_MODELS		1024
 +#define	SIN_MAX_MAP_BRUSHES		8192
 +#define	SIN_MAX_MAP_ENTITIES	2048
 +#define	SIN_MAX_MAP_ENTSTRING	0x40000
 +#define	SIN_MAX_MAP_TEXINFO		8192
 +
 +#define	SIN_MAX_MAP_AREAS		256
 +#define	SIN_MAX_MAP_AREAPORTALS	1024
 +#define	SIN_MAX_MAP_PLANES		65536
 +#define	SIN_MAX_MAP_NODES		65536
 +#define	SIN_MAX_MAP_BRUSHSIDES	65536
 +#define	SIN_MAX_MAP_LEAFS		65536
 +#define	SIN_MAX_MAP_VERTS		65536
 +#define	SIN_MAX_MAP_FACES		65536
 +#define	SIN_MAX_MAP_LEAFFACES	65536
 +#define	SIN_MAX_MAP_LEAFBRUSHES 65536
 +#define	SIN_MAX_MAP_PORTALS		65536
 +#define	SIN_MAX_MAP_EDGES		128000
 +#define	SIN_MAX_MAP_SURFEDGES	256000
 +#define	SIN_MAX_MAP_LIGHTING	0x320000
 +#define	SIN_MAX_MAP_VISIBILITY	0x280000
 +
 +#ifdef SIN
 +#define	SIN_MAX_MAP_LIGHTINFO		8192
 +#endif
 +
 +#ifdef SIN
 +#undef SIN_MAX_MAP_LIGHTING	//undef the Quake2 bsp version
 +#define	SIN_MAX_MAP_LIGHTING	0x300000
 +#endif
 +
 +#ifdef SIN
 +#undef SIN_MAX_MAP_VISIBILITY	//undef the Quake2 bsp version
 +#define	SIN_MAX_MAP_VISIBILITY	0x280000
 +#endif
 +
 +//=============================================================================
 +
 +typedef struct
 +{
 +	int		fileofs, filelen;
 +} sin_lump_t;
 +
 +#define	SIN_LUMP_ENTITIES		0
 +#define	SIN_LUMP_PLANES			1
 +#define	SIN_LUMP_VERTEXES		2
 +#define	SIN_LUMP_VISIBILITY		3
 +#define	SIN_LUMP_NODES			4
 +#define	SIN_LUMP_TEXINFO		5
 +#define	SIN_LUMP_FACES			6
 +#define	SIN_LUMP_LIGHTING		7
 +#define	SIN_LUMP_LEAFS			8
 +#define	SIN_LUMP_LEAFFACES		9
 +#define	SIN_LUMP_LEAFBRUSHES	10
 +#define	SIN_LUMP_EDGES			11
 +#define	SIN_LUMP_SURFEDGES		12
 +#define	SIN_LUMP_MODELS			13
 +#define	SIN_LUMP_BRUSHES		14
 +#define	SIN_LUMP_BRUSHSIDES		15
 +#define	SIN_LUMP_POP			16
 +#define	SIN_LUMP_AREAS			17
 +#define	SIN_LUMP_AREAPORTALS	18
 +
 +#ifdef SIN
 +#define	SIN_LUMP_LIGHTINFO 	19
 +#define	SINHEADER_LUMPS		20
 +#endif
 +
 +typedef struct
 +{
 +	int			ident;
 +	int			version;	
 +	sin_lump_t	lumps[SINHEADER_LUMPS];
 +} sin_dheader_t;
 +
 +typedef struct
 +{
 +	float		mins[3], maxs[3];
 +	float		origin[3];		// for sounds or lights
 +	int			headnode;
 +	int			firstface, numfaces;	// submodels just draw faces
 +										// without walking the bsp tree
 +} sin_dmodel_t;
 +
 +typedef struct
 +{
 +	float	point[3];
 +} sin_dvertex_t;
 +
 +
 +// 0-2 are axial planes
 +#define	PLANE_X			0
 +#define	PLANE_Y			1
 +#define	PLANE_Z			2
 +
 +// 3-5 are non-axial planes snapped to the nearest
 +#define	PLANE_ANYX		3
 +#define	PLANE_ANYY		4
 +#define	PLANE_ANYZ		5
 +
 +// planes (x&~1) and (x&~1)+1 are allways opposites
 +
 +typedef struct
 +{
 +	float	normal[3];
 +	float	dist;
 +	int		type;		// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
 +} sin_dplane_t;
 +
 +
 +// contents flags are seperate bits
 +// a given brush can contribute multiple content bits
 +// multiple brushes can be in a single leaf
 +
 +// these definitions also need to be in q_shared.h!
 +
 +// lower bits are stronger, and will eat weaker brushes completely
 +#ifdef SIN
 +#define	CONTENTS_FENCE			4
 +#endif
 +// remaining contents are non-visible, and don't eat brushes
 +
 +#ifdef SIN
 +#define	CONTENTS_DUMMYFENCE	0x1000
 +#endif
 +
 +#ifdef SIN
 +#define	SURF_MASKED		0x2		// surface texture is masked
 +#endif
 +
 +#define	SURF_SKY		0x4		// don't draw, but add to skybox
 +#define	SURF_WARP		0x8		// turbulent water warp
 +
 +#ifdef SIN
 +#define	SURF_NONLIT	   0x10	// surface is not lit
 +#define	SURF_NOFILTER  0x20	// surface is not bi-linear filtered
 +#endif
 +
 +#define	SURF_FLOWING	0x40	// scroll towards angle
 +#define	SURF_NODRAW		0x80	// don't bother referencing the texture
 +
 +#define	SURF_HINT		0x100	// make a primary bsp splitter
 +#define	SURF_SKIP		0x200	// completely ignore, allowing non-closed brushes
 +
 +#ifdef SIN
 +#define	SURF_CONVEYOR  0x40	// surface is not lit
 +#endif
 +
 +#ifdef SIN
 +#define	SURF_WAVY            0x400       // surface has waves
 +#define	SURF_RICOCHET		   0x800	      // projectiles bounce literally bounce off this surface
 +#define	SURF_PRELIT		      0x1000	   // surface has intensity information for pre-lighting
 +#define	SURF_MIRROR		      0x2000	   // surface is a mirror
 +#define	SURF_CONSOLE         0x4000	   // surface is a console
 +#define	SURF_USECOLOR        0x8000	   // surface is lit with non-lit * color
 +#define	SURF_HARDWAREONLY    0x10000     // surface has been damaged
 +#define	SURF_DAMAGE          0x20000     // surface can be damaged
 +#define	SURF_WEAK            0x40000     // surface has weak hit points
 +#define	SURF_NORMAL          0x80000     // surface has normal hit points
 +#define	SURF_ADD             0x100000    // surface will be additive
 +#define	SURF_ENVMAPPED       0x200000    // surface is envmapped
 +#define	SURF_RANDOMANIMATE   0x400000    // surface start animating on a random frame
 +#define	SURF_ANIMATE         0x800000    // surface animates
 +#define	SURF_RNDTIME         0x1000000   // time between animations is random
 +#define	SURF_TRANSLATE       0x2000000   // surface translates
 +#define	SURF_NOMERGE         0x4000000   // surface is not merged in csg phase
 +#define  SURF_TYPE_BIT0       0x8000000   // 0 bit of surface type
 +#define  SURF_TYPE_BIT1       0x10000000  // 1 bit of surface type
 +#define  SURF_TYPE_BIT2       0x20000000  // 2 bit of surface type
 +#define  SURF_TYPE_BIT3       0x40000000  // 3 bit of surface type
 +
 +#define SURF_START_BIT        27
 +#define SURFACETYPE_FROM_FLAGS( x ) ( ( x >> (SURF_START_BIT) ) & 0xf )
 +
 +
 +#define  SURF_TYPE_SHIFT(x)   ( (x) << (SURF_START_BIT) ) // macro for getting proper bit mask
 +
 +#define  SURF_TYPE_NONE       SURF_TYPE_SHIFT(0)
 +#define  SURF_TYPE_WOOD       SURF_TYPE_SHIFT(1)
 +#define  SURF_TYPE_METAL      SURF_TYPE_SHIFT(2)
 +#define  SURF_TYPE_STONE      SURF_TYPE_SHIFT(3)
 +#define  SURF_TYPE_CONCRETE   SURF_TYPE_SHIFT(4)
 +#define  SURF_TYPE_DIRT       SURF_TYPE_SHIFT(5)
 +#define  SURF_TYPE_FLESH      SURF_TYPE_SHIFT(6)
 +#define  SURF_TYPE_GRILL      SURF_TYPE_SHIFT(7)
 +#define  SURF_TYPE_GLASS      SURF_TYPE_SHIFT(8)
 +#define  SURF_TYPE_FABRIC     SURF_TYPE_SHIFT(9)
 +#define  SURF_TYPE_MONITOR    SURF_TYPE_SHIFT(10)
 +#define  SURF_TYPE_GRAVEL     SURF_TYPE_SHIFT(11)
 +#define  SURF_TYPE_VEGETATION SURF_TYPE_SHIFT(12)
 +#define  SURF_TYPE_PAPER      SURF_TYPE_SHIFT(13)
 +#define  SURF_TYPE_DUCT       SURF_TYPE_SHIFT(14)
 +#define  SURF_TYPE_WATER      SURF_TYPE_SHIFT(15)
 +#endif
 +
 +
 +typedef struct
 +{
 +	int			planenum;
 +	int			children[2];	// negative numbers are -(leafs+1), not nodes
 +	short		mins[3];		// for frustom culling
 +	short		maxs[3];
 +	unsigned short	firstface;
 +	unsigned short	numfaces;	// counting both sides
 +} sin_dnode_t;
 +
 +#ifdef SIN
 +
 +typedef struct sin_lightvalue_s
 +{
 +    int		value;			// light emission, etc
 +    vec3_t	color;
 +    float	direct;
 +    float	directangle;
 +    float	directstyle;
 +    char		directstylename[32];
 +} sin_lightvalue_t;
 +
 +typedef struct sin_texinfo_s
 +{
 +    float		vecs[2][4];		// [s/t][xyz offset]
 +    int			flags;			// miptex flags + overrides
 +    char		   texture[64];	// texture name (textures/*.wal)
 +    int			nexttexinfo;	// for animations, -1 = end of chain
 +    float      trans_mag;
 +    int        trans_angle;
 +    int        base_angle;
 +    float      animtime;
 +    float      nonlit;
 +    float      translucence;
 +    float      friction;
 +    float      restitution;
 +    vec3_t     color;
 +    char       groupname[32];
 +} sin_texinfo_t;
 +
 +#endif //SIN
 +
 +// note that edge 0 is never used, because negative edge nums are used for
 +// counterclockwise use of the edge in a face
 +typedef struct
 +{
 +	unsigned short	v[2];		// vertex numbers
 +} sin_dedge_t;
 +
 +#ifdef MAXLIGHTMAPS
 +#undef MAXLIGHTMAPS
 +#endif
 +#define	MAXLIGHTMAPS	16
 +typedef struct
 +{
 +	unsigned short	planenum;
 +	short		side;
 +
 +	int		firstedge;		// we must support > 64k edges
 +	short		numedges;	
 +	short		texinfo;
 +
 +// lighting info
 +	byte		styles[MAXLIGHTMAPS];
 +	int		lightofs;		// start of [numstyles*surfsize] samples
 +#ifdef SIN
 +   int      lightinfo;
 +#endif
 +} sin_dface_t;
 +
 +typedef struct
 +{
 +	int				contents;			// OR of all brushes (not needed?)
 +
 +	short			cluster;
 +	short			area;
 +
 +	short			mins[3];			// for frustum culling
 +	short			maxs[3];
 +
 +	unsigned short	firstleafface;
 +	unsigned short	numleaffaces;
 +
 +	unsigned short	firstleafbrush;
 +	unsigned short	numleafbrushes;
 +} sin_dleaf_t;
 +
 +typedef struct
 +{
 +	unsigned short	planenum;		// facing out of the leaf
 +	short	texinfo;
 +#ifdef SIN
 +   int lightinfo;
 +#endif
 +} sin_dbrushside_t;
 +
 +typedef struct
 +{
 +	int			firstside;
 +	int			numsides;
 +	int			contents;
 +} sin_dbrush_t;
 +
 +#define	ANGLE_UP	-1
 +#define	ANGLE_DOWN	-2
 +
 +
 +// the visibility lump consists of a header with a count, then
 +// byte offsets for the PVS and PHS of each cluster, then the raw
 +// compressed bit vectors
 +#define	DVIS_PVS	0
 +#define	DVIS_PHS	1
 +typedef struct
 +{
 +	int			numclusters;
 +	int			bitofs[8][2];	// bitofs[numclusters][2]
 +} sin_dvis_t;
 +
 +// each area has a list of portals that lead into other areas
 +// when portals are closed, other areas may not be visible or
 +// hearable even if the vis info says that it should be
 +typedef struct
 +{
 +	int		portalnum;
 +	int		otherarea;
 +} sin_dareaportal_t;
 +
 +typedef struct
 +{
 +	int		numareaportals;
 +	int		firstareaportal;
 +} sin_darea_t;
 diff --git a/code/bspc/tetrahedron.c b/code/bspc/tetrahedron.c new file mode 100755 index 0000000..36c10af --- /dev/null +++ b/code/bspc/tetrahedron.c @@ -0,0 +1,1389 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_mem.h"
 +#include "../botlib/aasfile.h"
 +#include "aas_store.h"
 +#include "aas_cfg.h"
 +#include "aas_file.h"
 +
 +//
 +// creating tetrahedrons from a arbitrary world bounded by triangles
 +//
 +// a triangle has 3 corners and 3 edges
 +// a tetrahedron is build out of 4 triangles
 +// a tetrahedron has 6 edges
 +// we start with a world bounded by triangles, a side of a triangle facing
 +// towards the oudside of the world is marked as part of tetrahedron -1
 +//
 +// a tetrahedron is defined by two non-coplanar triangles with a shared edge
 +//
 +// a tetrahedron is defined by one triangle and a vertex not in the triangle plane
 +//
 +// if all triangles using a specific vertex have tetrahedrons
 +// at both sides then this vertex will never be part of a new tetrahedron
 +//
 +// if all triangles using a specific edge have tetrahedrons
 +// at both sides then this vertex will never be part of a new tetrahedron
 +//
 +// each triangle can only be shared by two tetrahedrons
 +// when all triangles have tetrahedrons at both sides then we're done
 +//
 +// if we cannot create any new tetrahedrons and there is at least one triangle
 +// which has a tetrahedron only at one side then the world leaks
 +//
 +
 +#define Sign(x)		(x < 0 ? 1 : 0)
 +
 +#define MAX_TH_VERTEXES			128000
 +#define MAX_TH_PLANES			128000
 +#define MAX_TH_EDGES			512000
 +#define MAX_TH_TRIANGLES		51200
 +#define MAX_TH_TETRAHEDRONS		12800
 +
 +#define PLANEHASH_SIZE			1024
 +#define EDGEHASH_SIZE			1024
 +#define TRIANGLEHASH_SIZE		1024
 +#define VERTEXHASH_SHIFT		7
 +#define VERTEXHASH_SIZE			((MAX_MAP_BOUNDS>>(VERTEXHASH_SHIFT-1))+1)	//was 64
 +
 +#define	NORMAL_EPSILON			0.0001
 +#define DIST_EPSILON			0.1
 +#define VERTEX_EPSILON			0.01
 +#define INTEGRAL_EPSILON		0.01
 +
 +
 +//plane
 +typedef struct th_plane_s
 +{
 +	vec3_t normal;
 +	float dist;
 +	int type;
 +	int signbits;
 +	struct th_plane_s *hashnext;		//next plane in hash
 +} th_plane_t;
 +
 +//vertex
 +typedef struct th_vertex_s
 +{
 +	vec3_t v;
 +	int usercount;						//2x the number of to be processed
 +										//triangles using this vertex
 +	struct th_vertex_s *hashnext;		//next vertex in hash
 +} th_vertex_t;
 +
 +//edge
 +typedef struct th_edge_s
 +{
 +	int v[2];							//vertex indexes
 +	int usercount;						//number of to be processed
 +										//triangles using this edge
 +	struct th_edge_s *hashnext;			//next edge in hash
 +} th_edge_t;
 +
 +//triangle
 +typedef struct th_triangle_s
 +{
 +	int edges[3];						//negative if edge is flipped
 +	th_plane_t planes[3];				//triangle bounding planes
 +	int planenum;						//plane the triangle is in
 +	int front;							//tetrahedron at the front
 +	int back;							//tetrahedron at the back
 +	vec3_t mins, maxs;					//triangle bounding box
 +	struct th_triangle_s *prev, *next;	//links in linked triangle lists
 +	struct th_triangle_s *hashnext;		//next triangle in hash
 +} th_triangle_t;
 +
 +//tetrahedron
 +typedef struct th_tetrahedron_s
 +{
 +	int triangles[4];					//negative if at backside of triangle
 +	float volume;						//tetrahedron volume
 +} th_tetrahedron_t;
 +
 +typedef struct th_s
 +{
 +	//vertexes
 +	int numvertexes;
 +	th_vertex_t *vertexes;
 +	th_vertex_t *vertexhash[VERTEXHASH_SIZE * VERTEXHASH_SIZE];
 +	//planes
 +	int numplanes;
 +	th_plane_t *planes;
 +	th_plane_t *planehash[PLANEHASH_SIZE];
 +	//edges
 +	int numedges;
 +	th_edge_t *edges;
 +	th_edge_t *edgehash[EDGEHASH_SIZE];
 +	//triangles
 +	int numtriangles;
 +	th_triangle_t *triangles;
 +	th_triangle_t *trianglehash[TRIANGLEHASH_SIZE];
 +	//tetrahedrons
 +	int numtetrahedrons;
 +	th_tetrahedron_t *tetrahedrons;
 +} th_t;
 +
 +th_t thworld;
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_InitMaxTH(void)
 +{
 +	//get memory for the tetrahedron data
 +	thworld.vertexes = (th_vertex_t *) GetClearedMemory(MAX_TH_VERTEXES * sizeof(th_vertex_t));
 +	thworld.planes = (th_plane_t *) GetClearedMemory(MAX_TH_PLANES * sizeof(th_plane_t));
 +	thworld.edges = (th_edge_t *) GetClearedMemory(MAX_TH_EDGES * sizeof(th_edge_t));
 +	thworld.triangles = (th_triangle_t *) GetClearedMemory(MAX_TH_TRIANGLES * sizeof(th_triangle_t));
 +	thworld.tetrahedrons = (th_tetrahedron_t *) GetClearedMemory(MAX_TH_TETRAHEDRONS * sizeof(th_tetrahedron_t));
 +	//reset the hash tables
 +	memset(thworld.vertexhash, 0, VERTEXHASH_SIZE * sizeof(th_vertex_t *));
 +	memset(thworld.planehash, 0, PLANEHASH_SIZE * sizeof(th_plane_t *));
 +	memset(thworld.edgehash, 0, EDGEHASH_SIZE * sizeof(th_edge_t *));
 +	memset(thworld.trianglehash, 0, TRIANGLEHASH_SIZE * sizeof(th_triangle_t *));
 +} //end of the function TH_InitMaxTH
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_FreeMaxTH(void)
 +{
 +	if (thworld.vertexes) FreeMemory(thworld.vertexes);
 +	thworld.vertexes = NULL;
 +	thworld.numvertexes = 0;
 +	if (thworld.planes) FreeMemory(thworld.planes);
 +	thworld.planes = NULL;
 +	thworld.numplanes = 0;
 +	if (thworld.edges) FreeMemory(thworld.edges);
 +	thworld.edges = NULL;
 +	thworld.numedges = 0;
 +	if (thworld.triangles) FreeMemory(thworld.triangles);
 +	thworld.triangles = NULL;
 +	thworld.numtriangles = 0;
 +	if (thworld.tetrahedrons) FreeMemory(thworld.tetrahedrons);
 +	thworld.tetrahedrons = NULL;
 +	thworld.numtetrahedrons = 0;
 +} //end of the function TH_FreeMaxTH
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +float TH_TriangleArea(th_triangle_t *tri)
 +{
 +	return 0;
 +} //end of the function TH_TriangleArea
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +float TH_TetrahedronVolume(th_tetrahedron_t *tetrahedron)
 +{
 +	int edgenum, verts[3], i, j, v2;
 +	float volume, d;
 +	th_triangle_t *tri, *tri2;
 +	th_plane_t *plane;
 +
 +	tri = &thworld.triangles[abs(tetrahedron->triangles[0])];
 +	for (i = 0; i < 3; i++)
 +	{
 +		edgenum = tri->edges[i];
 +		if (edgenum < 0) verts[i] = thworld.edges[abs(edgenum)].v[1];
 +		else verts[i] = thworld.edges[edgenum].v[0];
 +	} //end for
 +	//
 +	tri2 = &thworld.triangles[abs(tetrahedron->triangles[1])];
 +	for (j = 0; j < 3; j++)
 +	{
 +		edgenum = tri2->edges[i];
 +		if (edgenum < 0) v2 = thworld.edges[abs(edgenum)].v[1];
 +		else v2 = thworld.edges[edgenum].v[0];
 +		if (v2 != verts[0] &&
 +			v2 != verts[1] &&
 +			v2 != verts[2]) break;
 +	} //end for
 +
 +	plane = &thworld.planes[tri->planenum];
 +	d = -(DotProduct (thworld.vertexes[v2].v, plane->normal) - plane->dist);
 +	volume = TH_TriangleArea(tri) * d / 3;
 +	return volume;
 +} //end of the function TH_TetrahedronVolume
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_PlaneSignBits(vec3_t normal)
 +{
 +	int i, signbits;
 +
 +	signbits = 0;
 +	for (i = 2; i >= 0; i--)
 +	{
 +		signbits = (signbits << 1) + Sign(normal[i]);
 +	} //end for
 +	return signbits;
 +} //end of the function TH_PlaneSignBits
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_PlaneTypeForNormal(vec3_t normal)
 +{
 +	vec_t	ax, ay, az;
 +	
 +// NOTE: should these have an epsilon around 1.0?		
 +	if (normal[0] == 1.0 || normal[0] == -1.0)
 +		return PLANE_X;
 +	if (normal[1] == 1.0 || normal[1] == -1.0)
 +		return PLANE_Y;
 +	if (normal[2] == 1.0 || normal[2] == -1.0)
 +		return PLANE_Z;
 +		
 +	ax = fabs(normal[0]);
 +	ay = fabs(normal[1]);
 +	az = fabs(normal[2]);
 +	
 +	if (ax >= ay && ax >= az)
 +		return PLANE_ANYX;
 +	if (ay >= ax && ay >= az)
 +		return PLANE_ANYY;
 +	return PLANE_ANYZ;
 +} //end of the function TH_PlaneTypeForNormal
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +qboolean TH_PlaneEqual(th_plane_t *p, vec3_t normal, vec_t dist)
 +{
 +	if (
 +	   fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
 +	&& fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
 +	&& fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
 +	&& fabs(p->dist - dist) < DIST_EPSILON )
 +		return true;
 +	return false;
 +} //end of the function TH_PlaneEqual
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AddPlaneToHash(th_plane_t *p)
 +{
 +	int hash;
 +
 +	hash = (int)fabs(p->dist) / 8;
 +	hash &= (PLANEHASH_SIZE-1);
 +
 +	p->hashnext = thworld.planehash[hash];
 +	thworld.planehash[hash] = p;
 +} //end of the function TH_AddPlaneToHash
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_CreateFloatPlane(vec3_t normal, vec_t dist)
 +{
 +	th_plane_t *p, temp;
 +
 +	if (VectorLength(normal) < 0.5)
 +		Error ("FloatPlane: bad normal");
 +	// create a new plane
 +	if (thworld.numplanes+2 > MAX_TH_PLANES)
 +		Error ("MAX_TH_PLANES");
 +
 +	p = &thworld.planes[thworld.numplanes];
 +	VectorCopy (normal, p->normal);
 +	p->dist = dist;
 +	p->type = (p+1)->type = TH_PlaneTypeForNormal (p->normal);
 +	p->signbits = TH_PlaneSignBits(p->normal);
 +
 +	VectorSubtract (vec3_origin, normal, (p+1)->normal);
 +	(p+1)->dist = -dist;
 +	(p+1)->signbits = TH_PlaneSignBits((p+1)->normal);
 +
 +	thworld.numplanes += 2;
 +
 +	// allways put axial planes facing positive first
 +	if (p->type < 3)
 +	{
 +		if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
 +		{
 +			// flip order
 +			temp = *p;
 +			*p = *(p+1);
 +			*(p+1) = temp;
 +
 +			TH_AddPlaneToHash(p);
 +			TH_AddPlaneToHash(p+1);
 +			return thworld.numplanes - 1;
 +		} //end if
 +	} //end if
 +
 +	TH_AddPlaneToHash(p);
 +	TH_AddPlaneToHash(p+1);
 +	return thworld.numplanes - 2;
 +} //end of the function TH_CreateFloatPlane
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_SnapVector(vec3_t normal)
 +{
 +	int		i;
 +
 +	for (i = 0; i < 3; i++)
 +	{
 +		if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
 +		{
 +			VectorClear (normal);
 +			normal[i] = 1;
 +			break;
 +		} //end if
 +		if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
 +		{
 +			VectorClear (normal);
 +			normal[i] = -1;
 +			break;
 +		} //end if
 +	} //end for
 +} //end of the function TH_SnapVector
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_SnapPlane(vec3_t normal, vec_t *dist)
 +{
 +	TH_SnapVector(normal);
 +
 +	if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
 +		*dist = Q_rint(*dist);
 +} //end of the function TH_SnapPlane
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindFloatPlane(vec3_t normal, vec_t dist)
 +{
 +	int i;
 +	th_plane_t *p;
 +	int hash, h;
 +
 +	TH_SnapPlane (normal, &dist);
 +	hash = (int)fabs(dist) / 8;
 +	hash &= (PLANEHASH_SIZE-1);
 +
 +	// search the border bins as well
 +	for (i = -1; i <= 1; i++)
 +	{
 +		h = (hash+i)&(PLANEHASH_SIZE-1);
 +		for (p = thworld.planehash[h]; p; p = p->hashnext)
 +		{
 +			if (TH_PlaneEqual(p, normal, dist))
 +			{
 +				return p - thworld.planes;
 +			} //end if
 +		} //end for
 +	} //end for
 +	return TH_CreateFloatPlane(normal, dist);
 +} //end of the function TH_FindFloatPlane
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_PlaneFromPoints(int v1, int v2, int v3)
 +{
 +	vec3_t t1, t2, normal;
 +	vec_t dist;
 +	float *p0, *p1, *p2;
 +
 +	p0 = thworld.vertexes[v1].v;
 +	p1 = thworld.vertexes[v2].v;
 +	p2 = thworld.vertexes[v3].v;
 +
 +	VectorSubtract(p0, p1, t1);
 +	VectorSubtract(p2, p1, t2);
 +	CrossProduct(t1, t2, normal);
 +	VectorNormalize(normal);
 +
 +	dist = DotProduct(p0, normal);
 +
 +	return TH_FindFloatPlane(normal, dist);
 +} //end of the function TH_PlaneFromPoints
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AddEdgeUser(int edgenum)
 +{
 +	th_edge_t *edge;
 +
 +	edge = &thworld.edges[abs(edgenum)];
 +	//increase edge user count
 +	edge->usercount++;
 +	//increase vertex user count as well
 +	thworld.vertexes[edge->v[0]].usercount++;
 +	thworld.vertexes[edge->v[1]].usercount++;
 +} //end of the function TH_AddEdgeUser
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_RemoveEdgeUser(int edgenum)
 +{
 +	th_edge_t *edge;
 +
 +	edge = &thworld.edges[abs(edgenum)];
 +	//decrease edge user count
 +	edge->usercount--;
 +	//decrease vertex user count as well
 +	thworld.vertexes[edge->v[0]].usercount--;
 +	thworld.vertexes[edge->v[1]].usercount--;
 +} //end of the function TH_RemoveEdgeUser
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_FreeTriangleEdges(th_triangle_t *tri)
 +{
 +	int i;
 +
 +	for (i = 0; i < 3; i++)
 +	{
 +		TH_RemoveEdgeUser(abs(tri->edges[i]));
 +	} //end for
 +} //end of the function TH_FreeTriangleEdges
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +unsigned TH_HashVec(vec3_t vec)
 +{
 +	int x, y;
 +
 +	x = (MAX_MAP_BOUNDS + (int)(vec[0]+0.5)) >> VERTEXHASH_SHIFT;
 +	y = (MAX_MAP_BOUNDS + (int)(vec[1]+0.5)) >> VERTEXHASH_SHIFT;
 +
 +	if (x < 0 || x >= VERTEXHASH_SIZE || y < 0 || y >= VERTEXHASH_SIZE)
 +		Error("HashVec: point %f %f %f outside valid range", vec[0], vec[1], vec[2]);
 +	
 +	return y*VERTEXHASH_SIZE + x;
 +} //end of the function TH_HashVec
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindVertex(vec3_t v)
 +{
 +	int i, h;
 +	th_vertex_t *vertex;
 +	vec3_t vert;
 +	
 +	for (i = 0; i < 3; i++)
 +	{
 +		if ( fabs(v[i] - Q_rint(v[i])) < INTEGRAL_EPSILON)
 +			vert[i] = Q_rint(v[i]);
 +		else
 +			vert[i] = v[i];
 +	} //end for
 +
 +	h = TH_HashVec(vert);
 +
 +	for (vertex = thworld.vertexhash[h]; vertex; vertex = vertex->hashnext)
 +	{
 +		if (fabs(vertex->v[0] - vert[0]) < VERTEX_EPSILON &&
 +			fabs(vertex->v[1] - vert[1]) < VERTEX_EPSILON &&
 +			fabs(vertex->v[2] - vert[2]) < VERTEX_EPSILON)
 +		{
 +			return vertex - thworld.vertexes;
 +		} //end if
 +	} //end for
 +	return 0;
 +} //end of the function TH_FindVertex
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AddVertexToHash(th_vertex_t *vertex)
 +{
 +	int hashvalue;
 +
 +	hashvalue = TH_HashVec(vertex->v);
 +	vertex->hashnext = thworld.vertexhash[hashvalue];
 +	thworld.vertexhash[hashvalue] = vertex;
 +} //end of the function TH_AddVertexToHash
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_CreateVertex(vec3_t v)
 +{
 +	if (thworld.numvertexes == 0) thworld.numvertexes = 1;
 +	if (thworld.numvertexes >= MAX_TH_VERTEXES)
 +		Error("MAX_TH_VERTEXES");
 +	VectorCopy(v, thworld.vertexes[thworld.numvertexes].v);
 +	thworld.vertexes[thworld.numvertexes].usercount = 0;
 +	TH_AddVertexToHash(&thworld.vertexes[thworld.numvertexes]);
 +	thworld.numvertexes++;
 +	return thworld.numvertexes-1;
 +} //end of the function TH_CreateVertex
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindOrCreateVertex(vec3_t v)
 +{
 +	int vertexnum;
 +
 +	vertexnum = TH_FindVertex(v);
 +	if (!vertexnum) vertexnum = TH_CreateVertex(v);
 +	return vertexnum;
 +} //end of the function TH_FindOrCreateVertex
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindEdge(int v1, int v2)
 +{
 +	int hashvalue;
 +	th_edge_t *edge;
 +
 +	hashvalue = (v1 + v2) & (EDGEHASH_SIZE-1);
 +
 +	for (edge = thworld.edgehash[hashvalue]; edge; edge = edge->hashnext)
 +	{
 +		if (edge->v[0] == v1 && edge->v[1] == v2) return edge - thworld.edges;
 +		if (edge->v[1] == v1 && edge->v[0] == v2) return -(edge - thworld.edges);
 +	} //end for
 +	return 0;
 +} //end of the function TH_FindEdge
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AddEdgeToHash(th_edge_t *edge)
 +{
 +	int hashvalue;
 +
 +	hashvalue = (edge->v[0] + edge->v[1]) & (EDGEHASH_SIZE-1);
 +	edge->hashnext = thworld.edgehash[hashvalue];
 +	thworld.edgehash[hashvalue] = edge;
 +} //end of the function TH_AddEdgeToHash
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_CreateEdge(int v1, int v2)
 +{
 +	th_edge_t *edge;
 +
 +	if (thworld.numedges == 0) thworld.numedges = 1;
 +	if (thworld.numedges >= MAX_TH_EDGES)
 +		Error("MAX_TH_EDGES");
 +	edge = &thworld.edges[thworld.numedges++];
 +	edge->v[0] = v1;
 +	edge->v[1] = v2;
 +	TH_AddEdgeToHash(edge);
 +	return thworld.numedges-1;
 +} //end of the function TH_CreateEdge
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindOrCreateEdge(int v1, int v2)
 +{
 +	int edgenum;
 +
 +	edgenum = TH_FindEdge(v1, v2);
 +	if (!edgenum) edgenum = TH_CreateEdge(v1, v2);
 +	return edgenum;
 +} //end of the function TH_FindOrCreateEdge
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindTriangle(int verts[3])
 +{
 +	int i, hashvalue, edges[3];
 +	th_triangle_t *tri;
 +
 +	for (i = 0; i < 3; i++)
 +	{
 +		edges[i] = TH_FindEdge(verts[i], verts[(i+1)%3]);
 +		if (!edges[i]) return false;
 +	} //end for
 +	hashvalue = (abs(edges[0]) + abs(edges[1]) + abs(edges[2])) & (TRIANGLEHASH_SIZE-1);
 +	for (tri = thworld.trianglehash[hashvalue]; tri; tri = tri->next)
 +	{
 +		for (i = 0; i < 3; i++)
 +		{
 +			if (abs(tri->edges[i]) != abs(edges[0]) &&
 +				abs(tri->edges[i]) != abs(edges[1]) &&
 +				abs(tri->edges[i]) != abs(edges[2])) break;
 +		} //end for
 +		if (i >= 3) return tri - thworld.triangles;
 +	} //end for
 +	return 0;
 +} //end of the function TH_FindTriangle
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AddTriangleToHash(th_triangle_t *tri)
 +{
 +	int hashvalue;
 +
 +	hashvalue = (abs(tri->edges[0]) + abs(tri->edges[1]) + abs(tri->edges[2])) & (TRIANGLEHASH_SIZE-1);
 +	tri->hashnext = thworld.trianglehash[hashvalue];
 +	thworld.trianglehash[hashvalue] = tri;
 +} //end of the function TH_AddTriangleToHash
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_CreateTrianglePlanes(int verts[3], th_plane_t *triplane, th_plane_t *planes)
 +{
 +	int i;
 +	vec3_t dir;
 +
 +	for (i = 0; i < 3; i++)
 +	{
 +		VectorSubtract(thworld.vertexes[verts[(i+1)%3]].v, thworld.vertexes[verts[i]].v, dir);
 +		CrossProduct(dir, triplane->normal, planes[i].normal);
 +		VectorNormalize(planes[i].normal);
 +		planes[i].dist = DotProduct(thworld.vertexes[verts[i]].v, planes[i].normal);
 +	} //end for
 +} //end of the function TH_CreateTrianglePlanes
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_CreateTriangle(int verts[3])
 +{
 +	th_triangle_t *tri;
 +	int i;
 +
 +	if (thworld.numtriangles == 0) thworld.numtriangles = 1;
 +	if (thworld.numtriangles >= MAX_TH_TRIANGLES)
 +		Error("MAX_TH_TRIANGLES");
 +	tri = &thworld.triangles[thworld.numtriangles++];
 +	for (i = 0; i < 3; i++)
 +	{
 +		tri->edges[i] = TH_FindOrCreateEdge(verts[i], verts[(i+1)%3]);
 +		TH_AddEdgeUser(abs(tri->edges[i]));
 +	} //end for
 +	tri->front = 0;
 +	tri->back = 0;
 +	tri->planenum = TH_PlaneFromPoints(verts[0], verts[1], verts[2]);
 +	tri->prev = NULL;
 +	tri->next = NULL;
 +	tri->hashnext = NULL;
 +	TH_CreateTrianglePlanes(verts, &thworld.planes[tri->planenum], tri->planes);
 +	TH_AddTriangleToHash(tri);
 +	ClearBounds(tri->mins, tri->maxs);
 +	for (i = 0; i < 3; i++)
 +	{
 +		AddPointToBounds(thworld.vertexes[verts[i]].v, tri->mins, tri->maxs);
 +	} //end for
 +	return thworld.numtriangles-1;
 +} //end of the function TH_CreateTriangle
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_CreateTetrahedron(int triangles[4])
 +{
 +	th_tetrahedron_t *tetrahedron;
 +	int i;
 +
 +	if (thworld.numtetrahedrons == 0) thworld.numtetrahedrons = 1;
 +	if (thworld.numtetrahedrons >= MAX_TH_TETRAHEDRONS)
 +		Error("MAX_TH_TETRAHEDRONS");
 +	tetrahedron = &thworld.tetrahedrons[thworld.numtetrahedrons++];
 +	for (i = 0; i < 4; i++)
 +	{
 +		tetrahedron->triangles[i] = triangles[i];
 +		if (thworld.triangles[abs(triangles[i])].front)
 +		{
 +			thworld.triangles[abs(triangles[i])].back = thworld.numtetrahedrons-1;
 +		} //end if
 +		else
 +		{
 +			thworld.triangles[abs(triangles[i])].front = thworld.numtetrahedrons-1;
 +		} //end else
 +	} //end for
 +	tetrahedron->volume = 0;
 +	return thworld.numtetrahedrons-1;
 +} //end of the function TH_CreateTetrahedron
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_IntersectTrianglePlanes(int v1, int v2, th_plane_t *triplane, th_plane_t *planes)
 +{
 +	float *p1, *p2, front, back, frac, d;
 +	int i, side, lastside;
 +	vec3_t mid;
 +
 +	p1 = thworld.vertexes[v1].v;
 +	p2 = thworld.vertexes[v2].v;
 +
 +	front = DotProduct(p1, triplane->normal) - triplane->dist;
 +	back = DotProduct(p2, triplane->normal) - triplane->dist;
 +	//if both points at the same side of the plane
 +	if (front < 0.1 && back < 0.1) return false;
 +	if (front > -0.1 && back > -0.1) return false;
 +	//
 +	frac = front/(front-back);
 +	mid[0] = p1[0] + (p2[0] - p1[0]) * frac;
 +	mid[1] = p1[1] + (p2[1] - p1[1]) * frac;
 +	mid[2] = p1[2] + (p2[2] - p1[2]) * frac;
 +	//if the mid point is at the same side of all the tri bounding planes
 +	lastside = 0;
 +	for (i = 0; i < 3; i++)
 +	{
 +		d = DotProduct(mid, planes[i].normal) - planes[i].dist;
 +		side = d < 0;
 +		if (i && side != lastside) return false;
 +		lastside = side;
 +	} //end for
 +	return true;
 +} //end of the function TH_IntersectTrianglePlanes
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_OutsideBoundingBox(int v1, int v2, vec3_t mins, vec3_t maxs)
 +{
 +	float *p1, *p2;
 +	int i;
 +
 +	p1 = thworld.vertexes[v1].v;
 +	p2 = thworld.vertexes[v2].v;
 +	//if both points are at the outer side of one of the bounding box planes
 +	for (i = 0; i < 3; i++)
 +	{
 +		if (p1[i] < mins[i] && p2[i] < mins[i]) return true;
 +		if (p1[i] > maxs[i] && p2[i] > maxs[i]) return true;
 +	} //end for
 +	return false;
 +} //end of the function TH_OutsideBoundingBox
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_TryEdge(int v1, int v2)
 +{
 +	int i, j, v;
 +	th_plane_t *plane;
 +	th_triangle_t *tri;
 +
 +	//if the edge already exists it must be valid
 +	if (TH_FindEdge(v1, v2)) return true;
 +	//test the edge with all existing triangles
 +	for (i = 1; i < thworld.numtriangles; i++)
 +	{
 +		tri = &thworld.triangles[i];
 +		//if triangle is enclosed by two tetrahedrons we don't have to test it
 +		//because the edge always has to go through another triangle of those
 +		//tetrahedrons first to reach the enclosed triangle
 +		if (tri->front && tri->back) continue;
 +		//if the edges is totally outside the triangle bounding box
 +		if (TH_OutsideBoundingBox(v1, v2, tri->mins, tri->maxs)) continue;
 +		//if one of the edge vertexes is used by this triangle
 +		for (j = 0; j < 3; j++)
 +		{
 +			v = thworld.edges[abs(tri->edges[j])].v[tri->edges[j] < 0];
 +			if (v == v1 || v == v2) break;
 +		} //end for
 +		if (j < 3) continue;
 +		//get the triangle plane
 +		plane = &thworld.planes[tri->planenum];
 +		//if the edge intersects with a triangle then it's not valid
 +		if (TH_IntersectTrianglePlanes(v1, v2, plane, tri->planes)) return false;
 +	} //end for
 +	return true;
 +} //end of the function TH_TryEdge
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_TryTriangle(int verts[3])
 +{
 +	th_plane_t planes[3], triplane;
 +	vec3_t t1, t2;
 +	float *p0, *p1, *p2;
 +	int i, j;
 +
 +	p0 = thworld.vertexes[verts[0]].v;
 +	p1 = thworld.vertexes[verts[1]].v;
 +	p2 = thworld.vertexes[verts[2]].v;
 +
 +	VectorSubtract(p0, p1, t1);
 +	VectorSubtract(p2, p1, t2);
 +	CrossProduct(t1, t2, triplane.normal);
 +	VectorNormalize(triplane.normal);
 +	triplane.dist = DotProduct(p0, triplane.normal);
 +	//
 +	TH_CreateTrianglePlanes(verts, &triplane, planes);
 +	//test if any existing edge intersects with this triangle
 +	for (i = 1; i < thworld.numedges; i++)
 +	{
 +		//if the edge is only used by triangles with tetrahedrons at both sides
 +		if (!thworld.edges[i].usercount) continue;
 +		//if one of the triangle vertexes is used by this edge
 +		for (j = 0; j < 3; j++)
 +		{
 +			if (verts[j] == thworld.edges[j].v[0] ||
 +				verts[j] == thworld.edges[j].v[1]) break;
 +		} //end for
 +		if (j < 3) continue;
 +		//if this edge intersects with the triangle
 +		if (TH_IntersectTrianglePlanes(thworld.edges[i].v[0], thworld.edges[i].v[1], &triplane, planes)) return false;
 +	} //end for
 +	return true;
 +} //end of the function TH_TryTriangle
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AddTriangleToList(th_triangle_t **trianglelist, th_triangle_t *tri)
 +{
 +	tri->prev = NULL;
 +	tri->next = *trianglelist;
 +	if (*trianglelist) (*trianglelist)->prev = tri;
 +	*trianglelist = tri;
 +} //end of the function TH_AddTriangleToList
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_RemoveTriangleFromList(th_triangle_t **trianglelist, th_triangle_t *tri)
 +{
 +	if (tri->next) tri->next->prev = tri->prev;
 +	if (tri->prev) tri->prev->next = tri->next;
 +	else *trianglelist = tri->next;
 +} //end of the function TH_RemoveTriangleFromList
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindTetrahedron1(th_triangle_t *tri, int *triangles)
 +{
 +	int i, j, edgenum, side, v1, v2, v3, v4;
 +	int verts1[3], verts2[3];
 +	th_triangle_t *tri2;
 +
 +	//find another triangle with a shared edge
 +	for (tri2 = tri->next; tri2; tri2 = tri2->next)
 +	{
 +		//if the triangles are in the same plane
 +		if ((tri->planenum & ~1) == (tri2->planenum & ~1)) continue;
 +		//try to find a shared edge
 +		for (i = 0; i < 3; i++)
 +		{
 +			edgenum = abs(tri->edges[i]);
 +			for (j = 0; j < 3; j++)
 +			{
 +				if (edgenum == abs(tri2->edges[j])) break;
 +			} //end for
 +			if (j < 3) break;
 +		} //end for
 +		//if the triangles have a shared edge
 +		if (i < 3)
 +		{
 +			edgenum = tri->edges[(i+1)%3];
 +			if (edgenum < 0) v1 = thworld.edges[abs(edgenum)].v[0];
 +			else v1 = thworld.edges[edgenum].v[1];
 +			edgenum = tri2->edges[(j+1)%3];
 +			if (edgenum < 0) v2 = thworld.edges[abs(edgenum)].v[0];
 +			else v2 = thworld.edges[edgenum].v[1];
 +			//try the new edge
 +			if (TH_TryEdge(v1, v2))
 +			{
 +				edgenum = tri->edges[i];
 +				side = edgenum < 0;
 +				//get the vertexes of the shared edge
 +				v3 = thworld.edges[abs(edgenum)].v[side];
 +				v4 = thworld.edges[abs(edgenum)].v[!side];
 +				//try the two new triangles
 +				verts1[0] = v1;
 +				verts1[1] = v2;
 +				verts1[2] = v3;
 +				triangles[2] = TH_FindTriangle(verts1);
 +				if (triangles[2] || TH_TryTriangle(verts1))
 +				{
 +					verts2[0] = v2;
 +					verts2[1] = v1;
 +					verts2[2] = v4;
 +					triangles[3] = TH_FindTriangle(verts2);
 +					if (triangles[3] || TH_TryTriangle(verts2))
 +					{
 +						triangles[0] = tri - thworld.triangles;
 +						triangles[1] = tri2 - thworld.triangles;
 +						if (!triangles[2]) triangles[2] = TH_CreateTriangle(verts1);
 +						if (!triangles[3]) triangles[3] = TH_CreateTriangle(verts2);
 +						return true;
 +					} //end if
 +				} //end if
 +			} //end if
 +		} //end if
 +	} //end for
 +	return false;
 +} //end of the function TH_FindTetrahedron
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_FindTetrahedron2(th_triangle_t *tri, int *triangles)
 +{
 +	int i, edgenum, v1, verts[3], triverts[3];
 +	float d;
 +	th_plane_t *plane;
 +
 +	//get the verts of this triangle
 +	for (i = 0; i < 3; i++)
 +	{
 +		edgenum = tri->edges[i];
 +		if (edgenum < 0) verts[i] = thworld.edges[abs(edgenum)].v[1];
 +		else verts[i] = thworld.edges[edgenum].v[0];
 +	} //end for
 +	//
 +	plane = &thworld.planes[tri->planenum];
 +	for (v1 = 0; v1 < thworld.numvertexes; v1++)
 +	{
 +		//if the vertex is only used by triangles with tetrahedrons at both sides
 +		if (!thworld.vertexes[v1].usercount) continue;
 +		//check if the vertex is not coplanar with the triangle
 +		d = DotProduct(thworld.vertexes[v1].v, plane->normal) - plane->dist;
 +		if (fabs(d) < 1) continue;
 +		//check if we can create edges from the triangle towards this new vertex
 +		for (i = 0; i < 3; i++)
 +		{
 +			if (v1 == verts[i]) break;
 +			if (!TH_TryEdge(v1, verts[i])) break;
 +		} //end for
 +		if (i < 3) continue;
 +		//check if the triangles are valid
 +		for (i = 0; i < 3; i++)
 +		{
 +			triverts[0] = v1;
 +			triverts[1] = verts[i];
 +			triverts[2] = verts[(i+1)%3];
 +			//if the triangle already exists then it is valid
 +			triangles[i] = TH_FindTriangle(triverts);
 +			if (!triangles[i])
 +			{
 +				if (!TH_TryTriangle(triverts)) break;
 +			} //end if
 +		} //end for
 +		if (i < 3) continue;
 +		//create the tetrahedron triangles using the new vertex
 +		for (i = 0; i < 3; i++)
 +		{
 +			if (!triangles[i])
 +			{
 +				triverts[0] = v1;
 +				triverts[1] = verts[i];
 +				triverts[2] = verts[(i+1)%3];
 +				triangles[i] = TH_CreateTriangle(triverts);
 +			} //end if
 +		} //end for
 +		//add the existing triangle
 +		triangles[3] = tri - thworld.triangles;
 +		//
 +		return true;
 +	} //end for
 +	return false;
 +} //end of the function TH_FindTetrahedron2
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_TetrahedralDecomposition(th_triangle_t *triangles)
 +{
 +	int i, thtriangles[4], numtriangles;
 +	th_triangle_t *donetriangles, *tri;
 +
 +	donetriangles = NULL;
 +
 +	/*
 +	numtriangles = 0;
 +	qprintf("%6d triangles", numtriangles);
 +	for (tri = triangles; tri; tri = triangles)
 +	{
 +		qprintf("\r%6d", numtriangles++);
 +		if (!TH_FindTetrahedron1(tri, thtriangles))
 +		{
 +//			if (!TH_FindTetrahedron2(tri, thtriangles))
 +			{
 +//				Error("triangle without tetrahedron");
 +				TH_RemoveTriangleFromList(&triangles, tri);
 +				continue;
 +			} //end if
 +		} //end if
 +		//create a tetrahedron from the triangles
 +		TH_CreateTetrahedron(thtriangles);
 +		//
 +		for (i = 0; i < 4; i++)
 +		{
 +			if (thworld.triangles[abs(thtriangles[i])].front &&
 +				thworld.triangles[abs(thtriangles[i])].back)
 +			{
 +				TH_RemoveTriangleFromList(&triangles, &thworld.triangles[abs(thtriangles[i])]);
 +				TH_AddTriangleToList(&donetriangles, &thworld.triangles[abs(thtriangles[i])]);
 +				TH_FreeTriangleEdges(&thworld.triangles[abs(thtriangles[i])]);
 +			} //end if
 +			else
 +			{
 +				TH_AddTriangleToList(&triangles, &thworld.triangles[abs(thtriangles[i])]);
 +			} //end else
 +		} //end for
 +	} //end for*/
 +	qprintf("%6d tetrahedrons", thworld.numtetrahedrons);
 +	do
 +	{
 +		do
 +		{
 +			numtriangles = 0;
 +			for (i = 1; i < thworld.numtriangles; i++)
 +			{
 +				tri = &thworld.triangles[i];
 +				if (tri->front && tri->back) continue;
 +				//qprintf("\r%6d", numtriangles++);
 +				if (!TH_FindTetrahedron1(tri, thtriangles))
 +				{
 +//					if (!TH_FindTetrahedron2(tri, thtriangles))
 +					{
 +						continue;
 +					} //end if
 +				} //end if
 +				numtriangles++;
 +				//create a tetrahedron from the triangles
 +				TH_CreateTetrahedron(thtriangles);
 +				qprintf("\r%6d", thworld.numtetrahedrons);
 +			} //end for
 +		} while(numtriangles);
 + 		for (i = 1; i < thworld.numtriangles; i++)
 +		{
 +			tri = &thworld.triangles[i];
 +			if (tri->front && tri->back) continue;
 +			//qprintf("\r%6d", numtriangles++);
 +//			if (!TH_FindTetrahedron1(tri, thtriangles))
 +			{
 +				if (!TH_FindTetrahedron2(tri, thtriangles))
 +				{
 +					continue;
 +				} //end if
 +			} //end if
 +			numtriangles++;
 +			//create a tetrahedron from the triangles
 +			TH_CreateTetrahedron(thtriangles);
 +			qprintf("\r%6d", thworld.numtetrahedrons);
 +		} //end for
 +	} while(numtriangles);
 +	//
 +	numtriangles = 0;
 +	for (i = 1; i < thworld.numtriangles; i++)
 +	{
 +		tri = &thworld.triangles[i];
 +		if (!tri->front && !tri->back) numtriangles++;
 +	} //end for
 +	Log_Print("\r%6d triangles with front only\n", numtriangles);
 +	Log_Print("\r%6d tetrahedrons\n", thworld.numtetrahedrons-1);
 +} //end of the function TH_TetrahedralDecomposition
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AASFaceVertex(aas_face_t *face, int index, vec3_t vertex)
 +{
 +	int edgenum, side;
 +
 +	edgenum = aasworld.edgeindex[face->firstedge + index];
 +	side = edgenum < 0;
 +	VectorCopy(aasworld.vertexes[aasworld.edges[abs(edgenum)].v[side]], vertex);
 +} //end of the function TH_AASFaceVertex
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TH_Colinear(float *v0, float *v1, float *v2)
 +{
 +	vec3_t t1, t2, vcross;
 +	float d;
 +	
 +	VectorSubtract(v1, v0, t1);
 +	VectorSubtract(v2, v0, t2);
 +	CrossProduct (t1, t2, vcross);
 +	d = VectorLength( vcross );
 +
 +	// if cross product is zero point is colinear
 +	if (d < 10)
 +	{
 +		return true;
 +	} //end if
 +	return false;
 +} //end of the function TH_Colinear
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_FaceCenter(aas_face_t *face, vec3_t center)
 +{
 +	int i, edgenum, side;
 +	aas_edge_t *edge;
 +
 +	VectorClear(center);
 +	for (i = 0; i < face->numedges; i++)
 +	{
 +		edgenum = abs(aasworld.edgeindex[face->firstedge + i]);
 +		side = edgenum < 0;
 +		edge = &aasworld.edges[abs(edgenum)];
 +		VectorAdd(aasworld.vertexes[edge->v[side]], center, center);
 +	} //end for
 +	VectorScale(center, 1.0 / face->numedges, center);
 +} //end of the function TH_FaceCenter
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +th_triangle_t *TH_CreateAASFaceTriangles(aas_face_t *face)
 +{
 +	int i, first, verts[3], trinum;
 +	vec3_t p0, p1, p2, p3, p4, center;
 +	th_triangle_t *tri, *triangles;
 +
 +	triangles = NULL;
 +	//find three points that are not colinear
 +	for (i = 0; i < face->numedges; i++)
 +	{
 +		TH_AASFaceVertex(face, (face->numedges + i-2)%face->numedges, p0);
 +		TH_AASFaceVertex(face, (face->numedges + i-1)%face->numedges, p1);
 +		TH_AASFaceVertex(face, (i  )%face->numedges, p2);
 +		if (TH_Colinear(p2, p0, p1)) continue;
 +		TH_AASFaceVertex(face, (i+1)%face->numedges, p3);
 +		TH_AASFaceVertex(face, (i+2)%face->numedges, p4);
 +		if (TH_Colinear(p2, p3, p4)) continue;
 +		break;
 +	} //end for
 +	//if there are three points that are not colinear
 +	if (i < face->numedges)
 +	{
 +		//normal triangulation
 +		first = i; //left and right most point of three non-colinear points
 +		TH_AASFaceVertex(face, first, p0);
 +		verts[0] = TH_FindOrCreateVertex(p0);
 +		for (i = 1; i < face->numedges-1; i++)
 +		{
 +			TH_AASFaceVertex(face, (first+i  )%face->numedges, p1);
 +			TH_AASFaceVertex(face, (first+i+1)%face->numedges, p2);
 +			verts[1] = TH_FindOrCreateVertex(p1);
 +			verts[2] = TH_FindOrCreateVertex(p2);
 +			trinum = TH_CreateTriangle(verts);
 +			tri = &thworld.triangles[trinum];
 +			tri->front = -1;
 +			TH_AddTriangleToList(&triangles, tri);
 +		} //end for
 +	} //end if
 +	else
 +	{
 +		//fan triangulation
 +		TH_FaceCenter(face, center);
 +		//
 +		verts[0] = TH_FindOrCreateVertex(center);
 +		for (i = 0; i < face->numedges; i++)
 +		{
 +			TH_AASFaceVertex(face, (i  )%face->numedges, p1);
 +			TH_AASFaceVertex(face, (i+1)%face->numedges, p2);
 +			if (TH_Colinear(center, p1, p2)) continue;
 +			verts[1] = TH_FindOrCreateVertex(p1);
 +			verts[2] = TH_FindOrCreateVertex(p2);
 +			trinum = TH_CreateTriangle(verts);
 +			tri = &thworld.triangles[trinum];
 +			tri->front = -1;
 +			TH_AddTriangleToList(&triangles, tri);
 +		} //end for
 +	} //end else
 +	return triangles;
 +} //end of the function TH_CreateAASFaceTriangles
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +th_triangle_t *TH_AASToTriangleMesh(void)
 +{
 +	int i, j, facenum, otherareanum;
 +	aas_face_t *face;
 +	th_triangle_t *tri, *nexttri, *triangles;
 +
 +	triangles = NULL;
 +	for (i = 1; i < aasworld.numareas; i++)
 +	{
 +		//if (!(aasworld.areasettings[i].presencetype & PRESENCE_NORMAL)) continue;
 +		for (j = 0; j < aasworld.areas[i].numfaces; j++)
 +		{
 +			facenum = abs(aasworld.faceindex[aasworld.areas[i].firstface + j]);
 +			face = &aasworld.faces[facenum];
 +			//only convert solid faces into triangles
 +			if (!(face->faceflags & FACE_SOLID))
 +			{
 +				/*
 +				if (face->frontarea == i) otherareanum = face->backarea;
 +				else otherareanum = face->frontarea;
 +				if (aasworld.areasettings[otherareanum].presencetype & PRESENCE_NORMAL) continue;
 +				*/
 +				continue;
 +			} //end if
 +			//
 +			tri = TH_CreateAASFaceTriangles(face);
 +			for (; tri; tri = nexttri)
 +			{
 +				nexttri = tri->next;
 +				TH_AddTriangleToList(&triangles, tri);
 +			} //end for
 +		} //end if
 +	} //end for
 +	return triangles;
 +} //end of the function TH_AASToTriangleMesh
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void TH_AASToTetrahedrons(char *filename)
 +{
 +	th_triangle_t *triangles, *tri, *lasttri;
 +	int cnt;
 +
 +	if (!AAS_LoadAASFile(filename, 0, 0))
 +		Error("couldn't load %s\n", filename);
 +
 +	//
 +	TH_InitMaxTH();
 +	//create a triangle mesh from the solid faces in the AAS file
 +	triangles = TH_AASToTriangleMesh();
 +	//
 +	cnt = 0;
 +	lasttri = NULL;
 +	for (tri = triangles; tri; tri = tri->next)
 +	{
 +		cnt++;
 +		if (tri->prev != lasttri) Log_Print("BAH\n");
 +		lasttri = tri;
 +	} //end for
 +	Log_Print("%6d triangles\n", cnt);
 +	//create a tetrahedral decomposition of the world bounded by triangles
 +	TH_TetrahedralDecomposition(triangles);
 +	//
 +	TH_FreeMaxTH();
 +} //end of the function TH_AASToTetrahedrons
 diff --git a/code/bspc/tetrahedron.h b/code/bspc/tetrahedron.h new file mode 100755 index 0000000..89003f1 --- /dev/null +++ b/code/bspc/tetrahedron.h @@ -0,0 +1,24 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +void TH_AASToTetrahedrons(char *filename);
 +
 diff --git a/code/bspc/textures.c b/code/bspc/textures.c new file mode 100755 index 0000000..ad6a8f5 --- /dev/null +++ b/code/bspc/textures.c @@ -0,0 +1,228 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +#include "l_bsp_q2.h"
 +
 +int nummiptex;
 +textureref_t textureref[MAX_MAP_TEXTURES];
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int FindMiptex (char *name)
 +{
 +	int i;
 +	char path[1024];
 +	miptex_t	*mt;
 +
 +	for (i = 0; i < nummiptex; i++)
 +	{
 +		if (!strcmp (name, textureref[i].name))
 +		{
 +			return i;
 +		} //end if
 +	} //end for
 +	if (nummiptex == MAX_MAP_TEXTURES)
 +		Error ("MAX_MAP_TEXTURES");
 +	strcpy (textureref[i].name, name);
 +
 +	// load the miptex to get the flags and values
 +	sprintf (path, "%stextures/%s.wal", gamedir, name);
 +	if (TryLoadFile (path, (void **)&mt) != -1)
 +	{
 +		textureref[i].value = LittleLong (mt->value);
 +		textureref[i].flags = LittleLong (mt->flags);
 +		textureref[i].contents = LittleLong (mt->contents);
 +		strcpy (textureref[i].animname, mt->animname);
 +		FreeMemory(mt);
 +	} //end if
 +	nummiptex++;
 +
 +	if (textureref[i].animname[0])
 +		FindMiptex (textureref[i].animname);
 +
 +	return i;
 +} //end of the function FindMipTex
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +vec3_t	baseaxis[18] =
 +{
 +{0,0,1}, {1,0,0}, {0,-1,0},		// floor
 +{0,0,-1}, {1,0,0}, {0,-1,0},		// ceiling
 +{1,0,0}, {0,1,0}, {0,0,-1},		// west wall
 +{-1,0,0}, {0,1,0}, {0,0,-1},		// east wall
 +{0,1,0}, {1,0,0}, {0,0,-1},		// south wall
 +{0,-1,0}, {1,0,0}, {0,0,-1}		// north wall
 +};
 +
 +void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
 +{
 +	int		bestaxis;
 +	vec_t	dot,best;
 +	int		i;
 +	
 +	best = 0;
 +	bestaxis = 0;
 +	
 +	for (i=0 ; i<6 ; i++)
 +	{
 +		dot = DotProduct (pln->normal, baseaxis[i*3]);
 +		if (dot > best)
 +		{
 +			best = dot;
 +			bestaxis = i;
 +		}
 +	}
 +	
 +	VectorCopy (baseaxis[bestaxis*3+1], xv);
 +	VectorCopy (baseaxis[bestaxis*3+2], yv);
 +} //end of the function TextureAxisFromPlane
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +int TexinfoForBrushTexture(plane_t *plane, brush_texture_t *bt, vec3_t origin)
 +{
 +	vec3_t	vecs[2];
 +	int		sv, tv;
 +	vec_t	ang, sinv, cosv;
 +	vec_t	ns, nt;
 +	texinfo_t	tx, *tc;
 +	int		i, j, k;
 +	float	shift[2];
 +	brush_texture_t		anim;
 +	int				mt;
 +
 +	if (!bt->name[0])
 +		return 0;
 +
 +	memset (&tx, 0, sizeof(tx));
 +	strcpy (tx.texture, bt->name);
 +
 +	TextureAxisFromPlane(plane, vecs[0], vecs[1]);
 +
 +	shift[0] = DotProduct (origin, vecs[0]);
 +	shift[1] = DotProduct (origin, vecs[1]);
 +
 +	if (!bt->scale[0])
 +		bt->scale[0] = 1;
 +	if (!bt->scale[1])
 +		bt->scale[1] = 1;
 +
 +
 +// rotate axis
 +	if (bt->rotate == 0)
 +		{ sinv = 0 ; cosv = 1; }
 +	else if (bt->rotate == 90)
 +		{ sinv = 1 ; cosv = 0; }
 +	else if (bt->rotate == 180)
 +		{ sinv = 0 ; cosv = -1; }
 +	else if (bt->rotate == 270)
 +		{ sinv = -1 ; cosv = 0; }
 +	else
 +	{	
 +		ang = bt->rotate / 180 * Q_PI;
 +		sinv = sin(ang);
 +		cosv = cos(ang);
 +	}
 +
 +	if (vecs[0][0])
 +		sv = 0;
 +	else if (vecs[0][1])
 +		sv = 1;
 +	else
 +		sv = 2;
 +				
 +	if (vecs[1][0])
 +		tv = 0;
 +	else if (vecs[1][1])
 +		tv = 1;
 +	else
 +		tv = 2;
 +					
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
 +		nt = sinv * vecs[i][sv] +  cosv * vecs[i][tv];
 +		vecs[i][sv] = ns;
 +		vecs[i][tv] = nt;
 +	}
 +
 +	for (i=0 ; i<2 ; i++)
 +		for (j=0 ; j<3 ; j++)
 +			tx.vecs[i][j] = vecs[i][j] / bt->scale[i];
 +
 +	tx.vecs[0][3] = bt->shift[0] + shift[0];
 +	tx.vecs[1][3] = bt->shift[1] + shift[1];
 +	tx.flags = bt->flags;
 +	tx.value = bt->value;
 +
 +	//
 +	// find the texinfo
 +	//
 +	tc = texinfo;
 +	for (i=0 ; i<numtexinfo ; i++, tc++)
 +	{
 +		if (tc->flags != tx.flags)
 +			continue;
 +		if (tc->value != tx.value)
 +			continue;
 +		for (j=0 ; j<2 ; j++)
 +		{
 +			if (strcmp (tc->texture, tx.texture))
 +				goto skip;
 +			for (k=0 ; k<4 ; k++)
 +			{
 +				if (tc->vecs[j][k] != tx.vecs[j][k])
 +					goto skip;
 +			}
 +		}
 +		return i;
 +skip:;
 +	}
 +	*tc = tx;
 +	numtexinfo++;
 +
 +	// load the next animation
 +	mt = FindMiptex (bt->name);
 +	if (textureref[mt].animname[0])
 +	{
 +		anim = *bt;
 +		strcpy (anim.name, textureref[mt].animname);
 +		tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin);
 +	}
 +	else
 +		tc->nexttexinfo = -1;
 +
 +
 +	return i;
 +} //end of the function TexinfoForBrushTexture
 diff --git a/code/bspc/tree.c b/code/bspc/tree.c new file mode 100755 index 0000000..3d2ee9c --- /dev/null +++ b/code/bspc/tree.c @@ -0,0 +1,283 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +
 +#include "qbsp.h"
 +
 +extern int c_nodes;
 +int	c_pruned;
 +int freedtreemem = 0;
 +
 +void RemovePortalFromNode (portal_t *portal, node_t *l);
 +
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +node_t *NodeForPoint (node_t *node, vec3_t origin)
 +{
 +	plane_t	*plane;
 +	vec_t	d;
 +
 +	while (node->planenum != PLANENUM_LEAF)
 +	{
 +		plane = &mapplanes[node->planenum];
 +		d = DotProduct (origin, plane->normal) - plane->dist;
 +		if (d >= 0)
 +			node = node->children[0];
 +		else
 +			node = node->children[1];
 +	}
 +	return node;
 +} //end of the function NodeForPoint
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Tree_FreePortals_r (node_t *node)
 +{
 +	portal_t	*p, *nextp;
 +	int			s;
 +
 +	// free children
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		Tree_FreePortals_r(node->children[0]);
 +		Tree_FreePortals_r(node->children[1]);
 +	}
 +
 +	// free portals
 +	for (p = node->portals; p; p = nextp)
 +	{
 +		s = (p->nodes[1] == node);
 +		nextp = p->next[s];
 +
 +		RemovePortalFromNode (p, p->nodes[!s]);
 +#ifdef ME
 +		if (p->winding) freedtreemem += MemorySize(p->winding);
 +		freedtreemem += MemorySize(p);
 +#endif //ME
 +		FreePortal(p);
 +	}
 +	node->portals = NULL;
 +} //end of the function Tree_FreePortals_r
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Tree_Free_r (node_t *node)
 +{
 +//	face_t *f, *nextf;
 +	bspbrush_t *brush, *nextbrush;
 +
 +	//free children
 +	if (node->planenum != PLANENUM_LEAF)
 +	{
 +		Tree_Free_r (node->children[0]);
 +		Tree_Free_r (node->children[1]);
 +	} //end if
 +	//free bspbrushes
 +//	FreeBrushList (node->brushlist);
 +	for (brush = node->brushlist; brush; brush = nextbrush)
 +	{
 +		nextbrush = brush->next;
 +#ifdef ME
 +		freedtreemem += MemorySize(brush);
 +#endif //ME
 +		FreeBrush(brush);
 +	} //end for
 +	node->brushlist = NULL;
 +
 +	/*
 +	NOTE: only used when creating Q2 bsp
 +	// free faces
 +	for (f = node->faces; f; f = nextf)
 +	{
 +		nextf = f->next;
 +#ifdef ME
 +		if (f->w) freedtreemem += MemorySize(f->w);
 +		freedtreemem += sizeof(face_t);
 +#endif //ME
 +		FreeFace(f);
 +	} //end for
 +	*/
 +
 +	// free the node
 +	if (node->volume)
 +	{
 +#ifdef ME
 +		freedtreemem += MemorySize(node->volume);
 +#endif //ME
 +		FreeBrush (node->volume);
 +	} //end if
 +
 +	if (numthreads == 1) c_nodes--;
 +#ifdef ME
 +	freedtreemem += MemorySize(node);
 +#endif //ME
 +	FreeMemory(node);
 +} //end of the function Tree_Free_r
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Tree_Free(tree_t *tree)
 +{
 +	//if no tree just return
 +	if (!tree) return;
 +	//
 +	freedtreemem = 0;
 +	//
 +	Tree_FreePortals_r(tree->headnode);
 +	Tree_Free_r(tree->headnode);
 +#ifdef ME
 +	freedtreemem += MemorySize(tree);
 +#endif //ME
 +	FreeMemory(tree);
 +#ifdef ME
 +	Log_Print("freed ");
 +	PrintMemorySize(freedtreemem);
 +	Log_Print(" of tree memory\n");
 +#endif //ME
 +} //end of the function Tree_Free
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +tree_t *Tree_Alloc(void)
 +{
 +	tree_t	*tree;
 +
 +	tree = GetMemory(sizeof(*tree));
 +	memset (tree, 0, sizeof(*tree));
 +	ClearBounds (tree->mins, tree->maxs);
 +
 +	return tree;
 +} //end of the function Tree_Alloc
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Tree_Print_r (node_t *node, int depth)
 +{
 +	int		i;
 +	plane_t	*plane;
 +	bspbrush_t	*bb;
 +
 +	for (i=0 ; i<depth ; i++)
 +		printf ("  ");
 +	if (node->planenum == PLANENUM_LEAF)
 +	{
 +		if (!node->brushlist)
 +			printf ("NULL\n");
 +		else
 +		{
 +			for (bb=node->brushlist ; bb ; bb=bb->next)
 +				printf ("%i ", bb->original->brushnum);
 +			printf ("\n");
 +		}
 +		return;
 +	}
 +
 +	plane = &mapplanes[node->planenum];
 +	printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
 +		plane->normal[0], plane->normal[1], plane->normal[2],
 +		plane->dist);
 +	Tree_Print_r (node->children[0], depth+1);
 +	Tree_Print_r (node->children[1], depth+1);
 +} //end of the function Tree_Print_r
 +//===========================================================================
 +// NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Tree_PruneNodes_r (node_t *node)
 +{
 +	bspbrush_t *b, *next;
 +
 +	if (node->planenum == PLANENUM_LEAF) return;
 +
 +	Tree_PruneNodes_r (node->children[0]);
 +	Tree_PruneNodes_r (node->children[1]);
 +
 +	if (create_aas)
 +	{
 +		if ((node->children[0]->contents & CONTENTS_LADDER) ||
 +				(node->children[1]->contents & CONTENTS_LADDER)) return;
 +	}
 +
 +	if ((node->children[0]->contents & CONTENTS_SOLID)
 +		&& (node->children[1]->contents & CONTENTS_SOLID))
 +	{
 +		if (node->faces)
 +			Error ("node->faces seperating CONTENTS_SOLID");
 +		if (node->children[0]->faces || node->children[1]->faces)
 +			Error ("!node->faces with children");
 +		// FIXME: free stuff
 +		node->planenum = PLANENUM_LEAF;
 +		node->contents = CONTENTS_SOLID;
 +		node->detail_seperator = false;
 +
 +		if (node->brushlist)
 +			Error ("PruneNodes: node->brushlist");
 +		// combine brush lists
 +		node->brushlist = node->children[1]->brushlist;
 +
 +		for (b = node->children[0]->brushlist; b; b = next)
 +		{
 +			next = b->next;
 +			b->next = node->brushlist;
 +			node->brushlist = b;
 +		} //end for
 +		//free the child nodes
 +		FreeMemory(node->children[0]);
 +		FreeMemory(node->children[1]);
 +		//two nodes are cut away
 +		c_pruned += 2;
 +	} //end if
 +} //end of the function Tree_PruneNodes_r
 +//===========================================================================
 +//
 +// Parameter:			-
 +// Returns:				-
 +// Changes Globals:		-
 +//===========================================================================
 +void Tree_PruneNodes(node_t *node)
 +{
 +	Log_Print("------- Prune Nodes --------\n");
 +	c_pruned = 0;
 +	Tree_PruneNodes_r(node);
 +	Log_Print("%5i pruned nodes\n", c_pruned);
 +} //end of the function Tree_PruneNodes
 diff --git a/code/bspc/writebsp.c b/code/bspc/writebsp.c new file mode 100755 index 0000000..38e9980 --- /dev/null +++ b/code/bspc/writebsp.c @@ -0,0 +1,595 @@ +/*
 +===========================================================================
 +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
 +===========================================================================
 +*/
 +#include "qbsp.h"
 +
 +int		c_nofaces;
 +int		c_facenodes;
 +
 +
 +/*
 +=========================================================
 +
 +ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
 +
 +=========================================================
 +*/
 +
 +int		planeused[MAX_MAP_PLANES];
 +
 +/*
 +============
 +EmitPlanes
 +
 +There is no oportunity to discard planes, because all of the original
 +brushes will be saved in the map.
 +============
 +*/
 +void EmitPlanes (void)
 +{
 +	int			i;
 +	dplane_t	*dp;
 +	plane_t		*mp;
 +	//ME: this causes a crash??
 +//	int		planetranslate[MAX_MAP_PLANES];
 +
 +	mp = mapplanes;
 +	for (i=0 ; i<nummapplanes ; i++, mp++)
 +	{
 +		dp = &dplanes[numplanes];
 +//		planetranslate[i] = numplanes;
 +		VectorCopy ( mp->normal, dp->normal);
 +		dp->dist = mp->dist;
 +		dp->type = mp->type;
 +		numplanes++;
 +		if (numplanes >= MAX_MAP_PLANES)
 +			Error("MAX_MAP_PLANES");
 +	}
 +}
 +
 +
 +//========================================================
 +
 +void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
 +{
 +	int			i;
 +	int			facenum;
 +
 +	while (f->merged)
 +		f = f->merged;
 +
 +	if (f->split[0])
 +	{
 +		EmitMarkFace (leaf_p, f->split[0]);
 +		EmitMarkFace (leaf_p, f->split[1]);
 +		return;
 +	}
 +
 +	facenum = f->outputnumber;
 +	if (facenum == -1)
 +		return;	// degenerate face
 +
 +	if (facenum < 0 || facenum >= numfaces)
 +		Error ("Bad leafface");
 +	for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
 +		if (dleaffaces[i] == facenum)
 +			break;		// merged out face
 +	if (i == numleaffaces)
 +	{
 +		if (numleaffaces >= MAX_MAP_LEAFFACES)
 +			Error ("MAX_MAP_LEAFFACES");
 +
 +		dleaffaces[numleaffaces] =  facenum;
 +		numleaffaces++;
 +	}
 +
 +}
 +
 +
 +/*
 +==================
 +EmitLeaf
 +==================
 +*/
 +void EmitLeaf (node_t *node)
 +{
 +	dleaf_t		*leaf_p;
 +	portal_t	*p;
 +	int			s;
 +	face_t		*f;
 +	bspbrush_t	*b;
 +	int			i;
 +	int			brushnum;
 +
 +	// emit a leaf
 +	if (numleafs >= MAX_MAP_LEAFS)
 +		Error ("MAX_MAP_LEAFS");
 +
 +	leaf_p = &dleafs[numleafs];
 +	numleafs++;
 +
 +	leaf_p->contents = node->contents;
 +	leaf_p->cluster = node->cluster;
 +	leaf_p->area = node->area;
 +
 +	//
 +	// write bounding box info
 +	//	
 +	VectorCopy (node->mins, leaf_p->mins);
 +	VectorCopy (node->maxs, leaf_p->maxs);
 +	
 +	//
 +	// write the leafbrushes
 +	//
 +	leaf_p->firstleafbrush = numleafbrushes;
 +	for (b=node->brushlist ; b ; b=b->next)
 +	{
 +		if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
 +			Error ("MAX_MAP_LEAFBRUSHES");
 +
 +		brushnum = b->original - mapbrushes;
 +		for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
 +			if (dleafbrushes[i] == brushnum)
 +				break;
 +		if (i == numleafbrushes)
 +		{
 +			dleafbrushes[numleafbrushes] = brushnum;
 +			numleafbrushes++;
 +		}
 +	}
 +	leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
 +
 +	//
 +	// write the leaffaces
 +	//
 +	if (leaf_p->contents & CONTENTS_SOLID)
 +		return;		// no leaffaces in solids
 +
 +	leaf_p->firstleafface = numleaffaces;
 +
 +	for (p = node->portals ; p ; p = p->next[s])	
 +	{
 +		s = (p->nodes[1] == node);
 +		f = p->face[s];
 +		if (!f)
 +			continue;	// not a visible portal
 +
 +		EmitMarkFace (leaf_p, f);
 +	}
 +	
 +	leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
 +}
 +
 +
 +/*
 +==================
 +EmitFace
 +==================
 +*/
 +void EmitFace (face_t *f)
 +{
 +	dface_t	*df;
 +	int		i;
 +	int		e;
 +
 +	f->outputnumber = -1;
 +
 +	if (f->numpoints < 3)
 +	{
 +		return;		// degenerated
 +	}
 +	if (f->merged || f->split[0] || f->split[1])
 +	{
 +		return;		// not a final face
 +	}
 +
 +	// save output number so leaffaces can use
 +	f->outputnumber = numfaces;
 +
 +	if (numfaces >= MAX_MAP_FACES)
 +		Error ("numfaces == MAX_MAP_FACES");
 +	df = &dfaces[numfaces];
 +	numfaces++;
 +
 +	// planenum is used by qlight, but not quake
 +	df->planenum = f->planenum & (~1);
 +	df->side = f->planenum & 1;
 +
 +	df->firstedge = numsurfedges;
 +	df->numedges = f->numpoints;
 +	df->texinfo = f->texinfo;
 +	for (i=0 ; i<f->numpoints ; i++)
 +	{
 +//		e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
 +		e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
 +		if (numsurfedges >= MAX_MAP_SURFEDGES)
 +			Error ("numsurfedges == MAX_MAP_SURFEDGES");
 +		dsurfedges[numsurfedges] = e;
 +		numsurfedges++;
 +	}
 +}
 +
 +/*
 +============
 +EmitDrawingNode_r
 +============
 +*/
 +int EmitDrawNode_r (node_t *node)
 +{
 +	dnode_t	*n;
 +	face_t	*f;
 +	int		i;
 +
 +	if (node->planenum == PLANENUM_LEAF)
 +	{
 +		EmitLeaf (node);
 +		return -numleafs;
 +	}
 +
 +	// emit a node	
 +	if (numnodes == MAX_MAP_NODES)
 +		Error ("MAX_MAP_NODES");
 +	n = &dnodes[numnodes];
 +	numnodes++;
 +
 +	VectorCopy (node->mins, n->mins);
 +	VectorCopy (node->maxs, n->maxs);
 +
 +	planeused[node->planenum]++;
 +	planeused[node->planenum^1]++;
 +
 +	if (node->planenum & 1)
 +		Error ("WriteDrawNodes_r: odd planenum");
 +	n->planenum = node->planenum;
 +	n->firstface = numfaces;
 +
 +	if (!node->faces)
 +		c_nofaces++;
 +	else
 +		c_facenodes++;
 +
 +	for (f=node->faces ; f ; f=f->next)
 +		EmitFace (f);
 +
 +	n->numfaces = numfaces - n->firstface;
 +
 +
 +	//
 +	// recursively output the other nodes
 +	//	
 +	for (i=0 ; i<2 ; i++)
 +	{
 +		if (node->children[i]->planenum == PLANENUM_LEAF)
 +		{
 +			n->children[i] = -(numleafs + 1);
 +			EmitLeaf (node->children[i]);
 +		}
 +		else
 +		{
 +			n->children[i] = numnodes;	
 +			EmitDrawNode_r (node->children[i]);
 +		}
 +	}
 +
 +	return n - dnodes;
 +}
 +
 +//=========================================================
 +
 +
 +/*
 +============
 +WriteBSP
 +============
 +*/
 +void WriteBSP (node_t *headnode)
 +{
 +	int		oldfaces;
 +
 +	c_nofaces = 0;
 +	c_facenodes = 0;
 +
 +	qprintf ("--- WriteBSP ---\n");
 +
 +	oldfaces = numfaces;
 +	dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
 +	EmitAreaPortals (headnode);
 +
 +	qprintf ("%5i nodes with faces\n", c_facenodes);
 +	qprintf ("%5i nodes without faces\n", c_nofaces);
 +	qprintf ("%5i faces\n", numfaces-oldfaces);
 +}
 +
 +//===========================================================
 +
 +/*
 +============
 +SetModelNumbers
 +============
 +*/
 +void SetModelNumbers (void)
 +{
 +	int		i;
 +	int		models;
 +	char	value[10];
 +
 +	models = 1;
 +	for (i=1 ; i<num_entities ; i++)
 +	{
 +		if (entities[i].numbrushes)
 +		{
 +			sprintf (value, "*%i", models);
 +			models++;
 +			SetKeyValue (&entities[i], "model", value);
 +		}
 +	}
 +
 +}
 +
 +/*
 +============
 +SetLightStyles
 +============
 +*/
 +#define	MAX_SWITCHED_LIGHTS	32
 +void SetLightStyles (void)
 +{
 +	int		stylenum;
 +	char	*t;
 +	entity_t	*e;
 +	int		i, j;
 +	char	value[10];
 +	char	lighttargets[MAX_SWITCHED_LIGHTS][64];
 +
 +
 +	// any light that is controlled (has a targetname)
 +	// must have a unique style number generated for it
 +
 +	stylenum = 0;
 +	for (i=1 ; i<num_entities ; i++)
 +	{
 +		e = &entities[i];
 +
 +		t = ValueForKey (e, "classname");
 +		if (Q_strncasecmp (t, "light", 5))
 +			continue;
 +		t = ValueForKey (e, "targetname");
 +		if (!t[0])
 +			continue;
 +		
 +		// find this targetname
 +		for (j=0 ; j<stylenum ; j++)
 +			if (!strcmp (lighttargets[j], t))
 +				break;
 +		if (j == stylenum)
 +		{
 +			if (stylenum == MAX_SWITCHED_LIGHTS)
 +				Error ("stylenum == MAX_SWITCHED_LIGHTS");
 +			strcpy (lighttargets[j], t);
 +			stylenum++;
 +		}
 +		sprintf (value, "%i", 32 + j);
 +		SetKeyValue (e, "style", value);
 +	}
 +
 +}
 +
 +//===========================================================
 +
 +/*
 +============
 +EmitBrushes
 +============
 +*/
 +void EmitBrushes (void)
 +{
 +	int			i, j, bnum, s, x;
 +	dbrush_t	*db;
 +	mapbrush_t		*b;
 +	dbrushside_t	*cp;
 +	vec3_t		normal;
 +	vec_t		dist;
 +	int			planenum;
 +
 +	numbrushsides = 0;
 +	numbrushes = nummapbrushes;
 +
 +	for (bnum=0 ; bnum<nummapbrushes ; bnum++)
 +	{
 +		b = &mapbrushes[bnum];
 +		db = &dbrushes[bnum];
 +
 +		db->contents = b->contents;
 +		db->firstside = numbrushsides;
 +		db->numsides = b->numsides;
 +		for (j=0 ; j<b->numsides ; j++)
 +		{
 +			if (numbrushsides == MAX_MAP_BRUSHSIDES)
 +				Error ("MAX_MAP_BRUSHSIDES");
 +			cp = &dbrushsides[numbrushsides];
 +			numbrushsides++;
 +			cp->planenum = b->original_sides[j].planenum;
 +			cp->texinfo = b->original_sides[j].texinfo;
 +		}
 +
 +#ifdef ME
 +	//for collision detection, bounding boxes are axial :)
 +	//brushes are convex so just add dot or line touching planes on the sides of
 +	//the brush parallell to the axis planes
 +#endif
 +		// add any axis planes not contained in the brush to bevel off corners
 +		for (x=0 ; x<3 ; x++)
 +			for (s=-1 ; s<=1 ; s+=2)
 +			{
 +			// add the plane
 +				VectorCopy (vec3_origin, normal);
 +				normal[x] = s;
 +				if (s == -1)
 +					dist = -b->mins[x];
 +				else
 +					dist = b->maxs[x];
 +				planenum = FindFloatPlane (normal, dist);
 +				for (i=0 ; i<b->numsides ; i++)
 +					if (b->original_sides[i].planenum == planenum)
 +						break;
 +				if (i == b->numsides)
 +				{
 +					if (numbrushsides >= MAX_MAP_BRUSHSIDES)
 +						Error ("MAX_MAP_BRUSHSIDES");
 +
 +					dbrushsides[numbrushsides].planenum = planenum;
 +					dbrushsides[numbrushsides].texinfo =
 +						dbrushsides[numbrushsides-1].texinfo;
 +					numbrushsides++;
 +					db->numsides++;
 +				}
 +			}
 +
 +	}
 +
 +}
 +
 +//===========================================================
 +
 +/*
 +==================
 +BeginBSPFile
 +==================
 +*/
 +void BeginBSPFile (void)
 +{
 +	// these values may actually be initialized
 +	// if the file existed when loaded, so clear them explicitly
 +	nummodels = 0;
 +	numfaces = 0;
 +	numnodes = 0;
 +	numbrushsides = 0;
 +	numvertexes = 0;
 +	numleaffaces = 0;
 +	numleafbrushes = 0;
 +	numsurfedges = 0;
 +
 +	// edge 0 is not used, because 0 can't be negated
 +	numedges = 1;
 +
 +	// leave vertex 0 as an error
 +	numvertexes = 1;
 +
 +	// leave leaf 0 as an error
 +	numleafs = 1;
 +	dleafs[0].contents = CONTENTS_SOLID;
 +}
 +
 +
 +/*
 +============
 +EndBSPFile
 +============
 +*/
 +void EndBSPFile (void)
 +{
 +#if 0
 +	char	path[1024];
 +	int		len;
 +	byte	*buf;
 +#endif
 +
 +
 +	EmitBrushes ();
 +	EmitPlanes ();
 +	Q2_UnparseEntities ();
 +
 +	// load the pop
 +#if 0
 +	sprintf (path, "%s/pics/pop.lmp", gamedir);
 +	len = LoadFile (path, &buf);
 +	memcpy (dpop, buf, sizeof(dpop));
 +	FreeMemory(buf);
 +#endif
 +}
 +
 +
 +/*
 +==================
 +BeginModel
 +==================
 +*/
 +int	firstmodleaf;
 +extern	int firstmodeledge;
 +extern	int	firstmodelface;
 +void BeginModel (void)
 +{
 +	dmodel_t	*mod;
 +	int			start, end;
 +	mapbrush_t	*b;
 +	int			j;
 +	entity_t	*e;
 +	vec3_t		mins, maxs;
 +
 +	if (nummodels == MAX_MAP_MODELS)
 +		Error ("MAX_MAP_MODELS");
 +	mod = &dmodels[nummodels];
 +
 +	mod->firstface = numfaces;
 +
 +	firstmodleaf = numleafs;
 +	firstmodeledge = numedges;
 +	firstmodelface = numfaces;
 +
 +	//
 +	// bound the brushes
 +	//
 +	e = &entities[entity_num];
 +
 +	start = e->firstbrush;
 +	end = start + e->numbrushes;
 +	ClearBounds (mins, maxs);
 +
 +	for (j=start ; j<end ; j++)
 +	{
 +		b = &mapbrushes[j];
 +		if (!b->numsides)
 +			continue;	// not a real brush (origin brush)
 +		AddPointToBounds (b->mins, mins, maxs);
 +		AddPointToBounds (b->maxs, mins, maxs);
 +	}
 +
 +	VectorCopy (mins, mod->mins);
 +	VectorCopy (maxs, mod->maxs);
 +}
 +
 +
 +/*
 +==================
 +EndModel
 +==================
 +*/
 +void EndModel (void)
 +{
 +	dmodel_t	*mod;
 +
 +	mod = &dmodels[nummodels];
 +
 +	mod->numfaces = numfaces - mod->firstface;
 +
 +	nummodels++;
 +}
 +
  | 
