aboutsummaryrefslogtreecommitdiffstats
path: root/code/bspc
diff options
context:
space:
mode:
Diffstat (limited to 'code/bspc')
-rwxr-xr-xcode/bspc/Conscript75
-rwxr-xr-xcode/bspc/Makefile114
-rwxr-xr-xcode/bspc/_files.c63
-rwxr-xr-xcode/bspc/aas_areamerging.c390
-rwxr-xr-xcode/bspc/aas_areamerging.h24
-rwxr-xr-xcode/bspc/aas_cfg.c252
-rwxr-xr-xcode/bspc/aas_cfg.h73
-rwxr-xr-xcode/bspc/aas_create.c1142
-rwxr-xr-xcode/bspc/aas_create.h136
-rwxr-xr-xcode/bspc/aas_edgemelting.c108
-rwxr-xr-xcode/bspc/aas_edgemelting.h24
-rwxr-xr-xcode/bspc/aas_facemerging.c282
-rwxr-xr-xcode/bspc/aas_facemerging.h24
-rwxr-xr-xcode/bspc/aas_file.c549
-rwxr-xr-xcode/bspc/aas_file.h25
-rwxr-xr-xcode/bspc/aas_gsubdiv.c656
-rwxr-xr-xcode/bspc/aas_gsubdiv.h25
-rwxr-xr-xcode/bspc/aas_map.c849
-rwxr-xr-xcode/bspc/aas_map.h23
-rwxr-xr-xcode/bspc/aas_prunenodes.c89
-rwxr-xr-xcode/bspc/aas_prunenodes.h24
-rwxr-xr-xcode/bspc/aas_store.c1082
-rwxr-xr-xcode/bspc/aas_store.h107
-rwxr-xr-xcode/bspc/aasfile.h252
-rwxr-xr-xcode/bspc/be_aas_bspc.c292
-rwxr-xr-xcode/bspc/be_aas_bspc.h23
-rwxr-xr-xcode/bspc/brushbsp.c1871
-rwxr-xr-xcode/bspc/bspc.c991
-rwxr-xr-xcode/bspc/bspc.sln28
-rwxr-xr-xcode/bspc/bspc.vcproj1381
-rwxr-xr-xcode/bspc/cfgq3.c84
-rwxr-xr-xcode/bspc/csg.c1005
-rwxr-xr-xcode/bspc/faces.c978
-rwxr-xr-xcode/bspc/gldraw.c232
-rwxr-xr-xcode/bspc/glfile.c149
-rwxr-xr-xcode/bspc/l_bsp_ent.c180
-rwxr-xr-xcode/bspc/l_bsp_ent.h58
-rwxr-xr-xcode/bspc/l_bsp_hl.c888
-rwxr-xr-xcode/bspc/l_bsp_hl.h314
-rwxr-xr-xcode/bspc/l_bsp_q1.c620
-rwxr-xr-xcode/bspc/l_bsp_q1.h275
-rwxr-xr-xcode/bspc/l_bsp_q2.c1134
-rwxr-xr-xcode/bspc/l_bsp_q2.h98
-rwxr-xr-xcode/bspc/l_bsp_q3.c824
-rwxr-xr-xcode/bspc/l_bsp_q3.h81
-rwxr-xr-xcode/bspc/l_bsp_sin.c1186
-rwxr-xr-xcode/bspc/l_bsp_sin.h106
-rwxr-xr-xcode/bspc/l_cmd.c1230
-rwxr-xr-xcode/bspc/l_cmd.h157
-rwxr-xr-xcode/bspc/l_log.c215
-rwxr-xr-xcode/bspc/l_log.h42
-rwxr-xr-xcode/bspc/l_math.c289
-rwxr-xr-xcode/bspc/l_math.h93
-rwxr-xr-xcode/bspc/l_mem.c441
-rwxr-xr-xcode/bspc/l_mem.h51
-rwxr-xr-xcode/bspc/l_poly.c1411
-rwxr-xr-xcode/bspc/l_poly.h120
-rwxr-xr-xcode/bspc/l_qfiles.c663
-rwxr-xr-xcode/bspc/l_qfiles.h91
-rwxr-xr-xcode/bspc/l_threads.c1510
-rwxr-xr-xcode/bspc/l_threads.h45
-rwxr-xr-xcode/bspc/l_utils.c259
-rwxr-xr-xcode/bspc/l_utils.h79
-rwxr-xr-xcode/bspc/lcc.mak61
-rwxr-xr-xcode/bspc/leakfile.c101
-rwxr-xr-xcode/bspc/linux-i386.mak109
-rwxr-xr-xcode/bspc/map.c1267
-rwxr-xr-xcode/bspc/map_hl.c1114
-rwxr-xr-xcode/bspc/map_q1.c1174
-rwxr-xr-xcode/bspc/map_q2.c1162
-rwxr-xr-xcode/bspc/map_q3.c681
-rwxr-xr-xcode/bspc/map_sin.c1211
-rwxr-xr-xcode/bspc/nodraw.c47
-rwxr-xr-xcode/bspc/portals.c1297
-rwxr-xr-xcode/bspc/prtfile.c287
-rwxr-xr-xcode/bspc/q2files.h487
-rwxr-xr-xcode/bspc/q3files.h374
-rwxr-xr-xcode/bspc/qbsp.h477
-rwxr-xr-xcode/bspc/qfiles.h487
-rwxr-xr-xcode/bspc/sinfiles.h365
-rwxr-xr-xcode/bspc/tetrahedron.c1389
-rwxr-xr-xcode/bspc/tetrahedron.h24
-rwxr-xr-xcode/bspc/textures.c228
-rwxr-xr-xcode/bspc/tree.c283
-rwxr-xr-xcode/bspc/writebsp.c595
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="&quot;$/source/code/bspc&quot;, 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++;
+}
+