From 6b5cfd96ea29fbd0ba1139a5055f67bd06b0e442 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 6 Nov 2010 16:48:43 +0100 Subject: [PATCH] bspc from quake3 1.32b source tarball. --- Conscript | 75 ++ Makefile | 114 +++ _files.c | 63 ++ aas_areamerging.c | 390 ++++++++++ aas_areamerging.h | 24 + aas_cfg.c | 252 ++++++ aas_cfg.h | 73 ++ aas_create.c | 1142 +++++++++++++++++++++++++++ aas_create.h | 136 ++++ aas_edgemelting.c | 108 +++ aas_edgemelting.h | 24 + aas_facemerging.c | 282 +++++++ aas_facemerging.h | 24 + aas_file.c | 549 +++++++++++++ aas_file.h | 25 + aas_gsubdiv.c | 656 ++++++++++++++++ aas_gsubdiv.h | 25 + aas_map.c | 849 ++++++++++++++++++++ aas_map.h | 23 + aas_prunenodes.c | 89 +++ aas_prunenodes.h | 24 + aas_store.c | 1082 ++++++++++++++++++++++++++ aas_store.h | 107 +++ aasfile.h | 252 ++++++ be_aas_bspc.c | 292 +++++++ be_aas_bspc.h | 23 + brushbsp.c | 1871 +++++++++++++++++++++++++++++++++++++++++++++ bspc.c | 991 ++++++++++++++++++++++++ bspc.sln | 28 + bspc.vcproj | 1381 +++++++++++++++++++++++++++++++++ cfgq3.c | 84 ++ csg.c | 1005 ++++++++++++++++++++++++ faces.c | 978 +++++++++++++++++++++++ gldraw.c | 232 ++++++ glfile.c | 149 ++++ l_bsp_ent.c | 180 +++++ l_bsp_ent.h | 58 ++ l_bsp_hl.c | 888 +++++++++++++++++++++ l_bsp_hl.h | 314 ++++++++ l_bsp_q1.c | 620 +++++++++++++++ l_bsp_q1.h | 275 +++++++ l_bsp_q2.c | 1134 +++++++++++++++++++++++++++ l_bsp_q2.h | 98 +++ l_bsp_q3.c | 824 ++++++++++++++++++++ l_bsp_q3.h | 81 ++ l_bsp_sin.c | 1186 ++++++++++++++++++++++++++++ l_bsp_sin.h | 106 +++ l_cmd.c | 1230 +++++++++++++++++++++++++++++ l_cmd.h | 157 ++++ l_log.c | 215 ++++++ l_log.h | 42 + l_math.c | 289 +++++++ l_math.h | 93 +++ l_mem.c | 441 +++++++++++ l_mem.h | 51 ++ l_poly.c | 1411 ++++++++++++++++++++++++++++++++++ l_poly.h | 120 +++ l_qfiles.c | 663 ++++++++++++++++ l_qfiles.h | 91 +++ l_threads.c | 1510 ++++++++++++++++++++++++++++++++++++ l_threads.h | 45 ++ l_utils.c | 259 +++++++ l_utils.h | 79 ++ lcc.mak | 61 ++ leakfile.c | 101 +++ linux-i386.mak | 109 +++ map.c | 1267 ++++++++++++++++++++++++++++++ map_hl.c | 1114 +++++++++++++++++++++++++++ map_q1.c | 1174 ++++++++++++++++++++++++++++ map_q2.c | 1162 ++++++++++++++++++++++++++++ map_q3.c | 681 +++++++++++++++++ map_sin.c | 1211 +++++++++++++++++++++++++++++ nodraw.c | 47 ++ portals.c | 1297 +++++++++++++++++++++++++++++++ prtfile.c | 287 +++++++ q2files.h | 487 ++++++++++++ q3files.h | 374 +++++++++ qbsp.h | 477 ++++++++++++ qfiles.h | 487 ++++++++++++ sinfiles.h | 365 +++++++++ tetrahedron.c | 1389 +++++++++++++++++++++++++++++++++ tetrahedron.h | 24 + textures.c | 228 ++++++ tree.c | 283 +++++++ writebsp.c | 595 ++++++++++++++ 85 files changed, 39102 insertions(+) create mode 100644 Conscript create mode 100644 Makefile create mode 100644 _files.c create mode 100644 aas_areamerging.c create mode 100644 aas_areamerging.h create mode 100644 aas_cfg.c create mode 100644 aas_cfg.h create mode 100644 aas_create.c create mode 100644 aas_create.h create mode 100644 aas_edgemelting.c create mode 100644 aas_edgemelting.h create mode 100644 aas_facemerging.c create mode 100644 aas_facemerging.h create mode 100644 aas_file.c create mode 100644 aas_file.h create mode 100644 aas_gsubdiv.c create mode 100644 aas_gsubdiv.h create mode 100644 aas_map.c create mode 100644 aas_map.h create mode 100644 aas_prunenodes.c create mode 100644 aas_prunenodes.h create mode 100644 aas_store.c create mode 100644 aas_store.h create mode 100644 aasfile.h create mode 100644 be_aas_bspc.c create mode 100644 be_aas_bspc.h create mode 100644 brushbsp.c create mode 100644 bspc.c create mode 100644 bspc.sln create mode 100644 bspc.vcproj create mode 100644 cfgq3.c create mode 100644 csg.c create mode 100644 faces.c create mode 100644 gldraw.c create mode 100644 glfile.c create mode 100644 l_bsp_ent.c create mode 100644 l_bsp_ent.h create mode 100644 l_bsp_hl.c create mode 100644 l_bsp_hl.h create mode 100644 l_bsp_q1.c create mode 100644 l_bsp_q1.h create mode 100644 l_bsp_q2.c create mode 100644 l_bsp_q2.h create mode 100644 l_bsp_q3.c create mode 100644 l_bsp_q3.h create mode 100644 l_bsp_sin.c create mode 100644 l_bsp_sin.h create mode 100644 l_cmd.c create mode 100644 l_cmd.h create mode 100644 l_log.c create mode 100644 l_log.h create mode 100644 l_math.c create mode 100644 l_math.h create mode 100644 l_mem.c create mode 100644 l_mem.h create mode 100644 l_poly.c create mode 100644 l_poly.h create mode 100644 l_qfiles.c create mode 100644 l_qfiles.h create mode 100644 l_threads.c create mode 100644 l_threads.h create mode 100644 l_utils.c create mode 100644 l_utils.h create mode 100644 lcc.mak create mode 100644 leakfile.c create mode 100644 linux-i386.mak create mode 100644 map.c create mode 100644 map_hl.c create mode 100644 map_q1.c create mode 100644 map_q2.c create mode 100644 map_q3.c create mode 100644 map_sin.c create mode 100644 nodraw.c create mode 100644 portals.c create mode 100644 prtfile.c create mode 100644 q2files.h create mode 100644 q3files.h create mode 100644 qbsp.h create mode 100644 qfiles.h create mode 100644 sinfiles.h create mode 100644 tetrahedron.c create mode 100644 tetrahedron.h create mode 100644 textures.c create mode 100644 tree.c create mode 100644 writebsp.c diff --git a/Conscript b/Conscript new file mode 100644 index 0000000..08487ab --- /dev/null +++ b/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/Makefile b/Makefile new file mode 100644 index 0000000..2f16169 --- /dev/null +++ b/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/_files.c b/_files.c new file mode 100644 index 0000000..8f4fcff --- /dev/null +++ b/_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/aas_areamerging.c b/aas_areamerging.c new file mode 100644 index 0000000..c5f82d2 --- /dev/null +++ b/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/aas_areamerging.h b/aas_areamerging.h new file mode 100644 index 0000000..c0b39a4 --- /dev/null +++ b/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/aas_cfg.c b/aas_cfg.c new file mode 100644 index 0000000..3aeb569 --- /dev/null +++ b/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/aas_cfg.h b/aas_cfg.h new file mode 100644 index 0000000..25537c6 --- /dev/null +++ b/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/aas_create.c b/aas_create.c new file mode 100644 index 0000000..914b25b --- /dev/null +++ b/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/aas_create.h b/aas_create.h new file mode 100644 index 0000000..cc5693c --- /dev/null +++ b/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/aas_edgemelting.c b/aas_edgemelting.c new file mode 100644 index 0000000..e84f48b --- /dev/null +++ b/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/aas_edgemelting.h b/aas_edgemelting.h new file mode 100644 index 0000000..4c03e97 --- /dev/null +++ b/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/aas_facemerging.c b/aas_facemerging.c new file mode 100644 index 0000000..bf170de --- /dev/null +++ b/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/aas_facemerging.h b/aas_facemerging.h new file mode 100644 index 0000000..5a81735 --- /dev/null +++ b/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/aas_file.c b/aas_file.c new file mode 100644 index 0000000..9f41639 --- /dev/null +++ b/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/aas_file.h b/aas_file.h new file mode 100644 index 0000000..5176461 --- /dev/null +++ b/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/aas_gsubdiv.c b/aas_gsubdiv.c new file mode 100644 index 0000000..d9ba597 --- /dev/null +++ b/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/aas_gsubdiv.h b/aas_gsubdiv.h new file mode 100644 index 0000000..0156a9a --- /dev/null +++ b/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/aas_map.c b/aas_map.c new file mode 100644 index 0000000..da16d1b --- /dev/null +++ b/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 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/aas_map.h b/aas_map.h new file mode 100644 index 0000000..601cdfb --- /dev/null +++ b/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/aas_prunenodes.c b/aas_prunenodes.c new file mode 100644 index 0000000..a03e9d2 --- /dev/null +++ b/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/aas_prunenodes.h b/aas_prunenodes.h new file mode 100644 index 0000000..36989ad --- /dev/null +++ b/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/aas_store.c b/aas_store.c new file mode 100644 index 0000000..4836d51 --- /dev/null +++ b/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/aas_store.h b/aas_store.h new file mode 100644 index 0000000..26957e4 --- /dev/null +++ b/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/aasfile.h b/aasfile.h new file mode 100644 index 0000000..7ed8677 --- /dev/null +++ b/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/be_aas_bspc.c b/be_aas_bspc.c new file mode 100644 index 0000000..127549c --- /dev/null +++ b/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/be_aas_bspc.h b/be_aas_bspc.h new file mode 100644 index 0000000..72e488a --- /dev/null +++ b/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/brushbsp.c b/brushbsp.c new file mode 100644 index 0000000..22cb0dd --- /dev/null +++ b/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 + +/* +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 ; inumsides ; 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 ; inumsides ; 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;inumsides ; 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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides ; i++) + { + side = &brush->sides[i]; + plane = &mapplanes[side->planenum]; + w = BaseWindingForPlane (plane->normal, plane->dist); + for (j=0 ; jnumsides && 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 ; inumsides ; 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 ; inumsides ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumsides ; 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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides && 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 ; inumsides ; 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 ; inumsides ; 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 ; inumsides ; 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/bspc.c b/bspc.c new file mode 100644 index 0000000..4f58b53 --- /dev/null +++ b/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 +#include +#include +#include +#else +#include +#include +#include +#include +#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 [- [- ...]]\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 = create AAS files for all BSPs\n" + " bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n" + " reach = compute reachability & clusters\n" + " cluster = compute clusters\n" + " aasopt = optimize aas file\n" + " aasinfo = show AAS file info\n" + " output = set output path\n" + " threads = set number of threads to X\n" + " cfg = 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 \n" + " = sets the micro volume to the given float\n" + " leaktest = perform a leak test\n" + " verboseentities\n" + " = enable entity verbose mode\n" + " chop \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/bspc.sln b/bspc.sln new file mode 100644 index 0000000..79dc8ae --- /dev/null +++ b/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/bspc.vcproj b/bspc.vcproj new file mode 100644 index 0000000..6171e1a --- /dev/null +++ b/bspc.vcproj @@ -0,0 +1,1381 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cfgq3.c b/cfgq3.c new file mode 100644 index 0000000..588509c --- /dev/null +++ b/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/csg.c b/csg.c new file mode 100644 index 0000000..c141075 --- /dev/null +++ b/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 ; inumsides && 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 ; inumsides ; i++) + { + for (j=0 ; jnumsides ; 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 ; inumsides ; 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 ; inumsides; + if (!numsides) + continue; + + // make sure the brush has at least one face showing + vis = 0; + for (j=0 ; joriginal_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 ; jsides[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 ; inumsides ; 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 ; inumsides ; 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 ; inumsides ; 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 ; inumsides ; 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/faces.c b/faces.c new file mode 100644 index 0000000..bcd3ef9 --- /dev/null +++ b/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]) 4096) + Error ("GetVertexnum: outside +/- 4096"); + } + + // search for an existing vertex match + for (i=0, dv=dvertexes ; ipoint[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 ; ivertexnums[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 ; ivertexnums[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 ; inumpoints ; 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> 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= 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 ; inumpoints ; 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 ; inumpoints ; 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 + 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 ; inumpoints ; 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/gldraw.c b/gldraw.c new file mode 100644 index 0000000..9f7d6f7 --- /dev/null +++ b/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 +#include +#include +#include + +#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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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/glfile.c b/glfile.c new file mode 100644 index 0000000..179a836 --- /dev/null +++ b/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 ; inumpoints ; 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/l_bsp_ent.c b/l_bsp_ent.c new file mode 100644 index 0000000..8260179 --- /dev/null +++ b/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/l_bsp_ent.h b/l_bsp_ent.h new file mode 100644 index 0000000..05dd162 --- /dev/null +++ b/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/l_bsp_hl.c b/l_bsp_hl.c new file mode 100644 index 0000000..5b922b5 --- /dev/null +++ b/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>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 ; inummiptex; + else + c = LittleLong(mtl->nummiptex); + mtl->nummiptex = LittleLong (mtl->nummiptex); + for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); + } + +// +// marksurfaces +// + for (i=0 ; ilumps[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 ; inext) + { + 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/l_bsp_hl.h b/l_bsp_hl.h new file mode 100644 index 0000000..0089b1e --- /dev/null +++ b/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/l_bsp_q1.c b/l_bsp_q1.c new file mode 100644 index 0000000..c68b5c8 --- /dev/null +++ b/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 ; iheadnode[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 ; inummiptex; + else + c = LittleLong(mtl->nummiptex); + mtl->nummiptex = LittleLong (mtl->nummiptex); + for (i=0 ; idataofs[i] = LittleLong(mtl->dataofs[i]); + } + +// +// marksurfaces +// + for (i=0 ; ilumps[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 ; inext) + { + 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/l_bsp_q1.h b/l_bsp_q1.h new file mode 100644 index 0000000..1c2cd92 --- /dev/null +++ b/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/l_bsp_q2.c b/l_bsp_q2.c new file mode 100644 index 0000000..d5e9844 --- /dev/null +++ b/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>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 ; ifirstface = 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 ; inumclusters; + else + j = LittleLong(dvis->numclusters); + dvis->numclusters = LittleLong (dvis->numclusters); + for (i=0 ; ibitofs[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 ; inext) + { + 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/l_bsp_q2.h b/l_bsp_q2.h new file mode 100644 index 0000000..8d937a8 --- /dev/null +++ b/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/l_bsp_q3.c b/l_bsp_q3.c new file mode 100644 index 0000000..19a3941 --- /dev/null +++ b/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 ; inext) + { + 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/l_bsp_q3.h b/l_bsp_q3.h new file mode 100644 index 0000000..6ac175c --- /dev/null +++ b/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/l_bsp_sin.c b/l_bsp_sin.c new file mode 100644 index 0000000..de51528 --- /dev/null +++ b/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>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 ; ifirstface = 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 ; inumclusters; + else + j = LittleLong(sin_dvis->numclusters); + sin_dvis->numclusters = LittleLong (sin_dvis->numclusters); + for (i=0 ; ibitofs[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 ; inext) + { + 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/l_bsp_sin.h b/l_bsp_sin.h new file mode 100644 index 0000000..93cb69f --- /dev/null +++ b/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/l_cmd.c b/l_cmd.c new file mode 100644 index 0000000..35fef7b --- /dev/null +++ b/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 +#include + +#ifndef SIN +#define SIN +#endif //SIN + +#if defined(WIN32) || defined(_WIN32) +#include +#else +#include +#endif + +#ifdef NeXT +#include +#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 + +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 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/l_cmd.h b/l_cmd.h new file mode 100644 index 0000000..b5497b4 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include + +#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/l_log.c b/l_log.c new file mode 100644 index 0000000..b685ed3 --- /dev/null +++ b/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 +#include +#include + +#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 \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/l_log.h b/l_log.h new file mode 100644 index 0000000..eff7bf6 --- /dev/null +++ b/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/l_math.c b/l_math.c new file mode 100644 index 0000000..caf3419 --- /dev/null +++ b/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/l_math.h b/l_math.h new file mode 100644 index 0000000..1df7b61 --- /dev/null +++ b/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 + +#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/l_mem.c b/l_mem.c new file mode 100644 index 0000000..831543e --- /dev/null +++ b/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/l_mem.h b/l_mem.h new file mode 100644 index 0000000..d517743 --- /dev/null +++ b/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/l_poly.c b/l_poly.c new file mode 100644 index 0000000..fea2bef --- /dev/null +++ b/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 +#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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; jnumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; jnumpoints ; 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/l_poly.h b/l_poly.h new file mode 100644 index 0000000..a14b834 --- /dev/null +++ b/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/l_qfiles.c b/l_qfiles.c new file mode 100644 index 0000000..32cb6de --- /dev/null +++ b/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 +#include +#include +#else +#include +#include +#include +#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/l_qfiles.h b/l_qfiles.h new file mode 100644 index 0000000..308c608 --- /dev/null +++ b/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/l_threads.c b/l_threads.c new file mode 100644 index 0000000..bc4b998 --- /dev/null +++ b/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 + +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 + +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) 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 +#include + +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) 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 +#include +#include +#include + +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) 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/l_threads.h b/l_threads.h new file mode 100644 index 0000000..2c6d8df --- /dev/null +++ b/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/l_utils.c b/l_utils.c new file mode 100644 index 0000000..f9d6d5f --- /dev/null +++ b/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/l_utils.h b/l_utils.h new file mode 100644 index 0000000..2ef9161 --- /dev/null +++ b/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/lcc.mak b/lcc.mak new file mode 100644 index 0000000..d74adce --- /dev/null +++ b/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/leakfile.c b/leakfile.c new file mode 100644 index 0000000..7118759 --- /dev/null +++ b/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/linux-i386.mak b/linux-i386.mak new file mode 100644 index 0000000..00a261d --- /dev/null +++ b/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/map.c b/map.c new file mode 100644 index 0000000..613c234 --- /dev/null +++ b/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 ; inumsides ; 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 ; inumsides ; i++) + { + s = b->original_sides + i; + w = s->winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; knumsides ; 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 ; lnumpoints ; 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 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/map_hl.c b/map_hl.c new file mode 100644 index 0000000..7e6037b --- /dev/null +++ b/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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides ; 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/map_q1.c b/map_q1.c new file mode 100644 index 0000000..772a358 --- /dev/null +++ b/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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides ; 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/map_q2.c b/map_q2.c new file mode 100644 index 0000000..0f15216 --- /dev/null +++ b/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 ; knumsides ; 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 ; inumsides ; 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 ; inumbrushes = 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 ; inumbrushes ; i++) + { + b = &mapbrushes[mapent->firstbrush + i]; + for (j=0 ; jnumsides ; 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 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 ; bnnumsides ; 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/map_q3.c b/map_q3.c new file mode 100644 index 0000000..750b304 --- /dev/null +++ b/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/map_sin.c b/map_sin.c new file mode 100644 index 0000000..7794b8c --- /dev/null +++ b/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 ; inumsides ; 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 ; knumsides ; 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 ; inumsides ; 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 ; inumbrushes = 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 ; jnumsides) + 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 ; inumbrushes ; i++) + { + b = &mapbrushes[mapent->firstbrush + i]; + for (j=0 ; jnumsides ; 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 ; inumbrushes ; 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 ; jnumsides ; 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 ; jnumsides ; 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 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; jnumsides && b->contents & CONTENTS_DETAIL) + num_detailbrushes++; + else if (b->numsides) + num_worldbrushes++; + for (k=0, s=b->original_sides ; knumsides ; 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/nodraw.c b/nodraw.c new file mode 100644 index 0000000..b5442dc --- /dev/null +++ b/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/portals.c b/portals.c new file mode 100644 index 0000000..3d3389d --- /dev/null +++ b/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 ; inumpoints ; 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 ; iwinding->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 ; jareaportalnum) + 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 ; inumsides ; 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 ; inumsides; + for (j=0 ; joriginal_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/prtfile.c b/prtfile.c new file mode 100644 index 0000000..69834aa --- /dev/null +++ b/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 ; inumpoints ; 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/q2files.h b/q2files.h new file mode 100644 index 0000000..5ffea6e --- /dev/null +++ b/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/q3files.h b/q3files.h new file mode 100644 index 0000000..b251cc0 --- /dev/null +++ b/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/qbsp.h b/qbsp.h new file mode 100644 index 0000000..7ebf9d4 --- /dev/null +++ b/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 +#endif +#include +#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/qfiles.h b/qfiles.h new file mode 100644 index 0000000..5ffea6e --- /dev/null +++ b/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/sinfiles.h b/sinfiles.h new file mode 100644 index 0000000..31091c5 --- /dev/null +++ b/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/tetrahedron.c b/tetrahedron.c new file mode 100644 index 0000000..36c10af --- /dev/null +++ b/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/tetrahedron.h b/tetrahedron.h new file mode 100644 index 0000000..89003f1 --- /dev/null +++ b/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/textures.c b/textures.c new file mode 100644 index 0000000..ad6a8f5 --- /dev/null +++ b/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 ; iflags != 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/tree.c b/tree.c new file mode 100644 index 0000000..3d2ee9c --- /dev/null +++ b/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 ; iplanenum == 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/writebsp.c b/writebsp.c new file mode 100644 index 0000000..38e9980 --- /dev/null +++ b/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 ; inormal, 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= 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 ; inumleafbrushes = 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 ; inumpoints ; 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 ; icontents = b->contents; + db->firstside = numbrushsides; + db->numsides = b->numsides; + for (j=0 ; jnumsides ; 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 ; inumsides ; 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 ; jnumsides) + 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++; +} +