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++; +} +