From 796b185d25cc00e25242af809c414e80af4bd330 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 6 Nov 2010 16:54:28 +0100 Subject: [PATCH] Convert Windows line endings to UNIX line endings. --- Conscript | 150 +- Makefile | 228 +-- aas_areamerging.c | 780 +++++----- aas_areamerging.h | 48 +- aas_cfg.c | 504 +++--- aas_cfg.h | 146 +- aas_create.c | 2284 +++++++++++++-------------- aas_create.h | 272 ++-- aas_edgemelting.c | 216 +-- aas_edgemelting.h | 48 +- aas_facemerging.c | 564 +++---- aas_facemerging.h | 48 +- aas_file.c | 1098 ++++++------- aas_file.h | 50 +- aas_gsubdiv.c | 1312 ++++++++-------- aas_gsubdiv.h | 50 +- aas_map.c | 1698 ++++++++++---------- aas_map.h | 46 +- aas_prunenodes.c | 178 +-- aas_prunenodes.h | 48 +- aas_store.c | 2164 +++++++++++++------------- aas_store.h | 214 +-- aasfile.h | 504 +++--- be_aas_bspc.c | 584 +++---- be_aas_bspc.h | 46 +- brushbsp.c | 3742 ++++++++++++++++++++++----------------------- bspc.c | 1982 ++++++++++++------------ bspc.sln | 56 +- bspc.vcproj | 2762 ++++++++++++++++----------------- cfgq3.c | 168 +- csg.c | 2010 ++++++++++++------------ faces.c | 1956 +++++++++++------------ gldraw.c | 464 +++--- glfile.c | 298 ++-- l_bsp_ent.c | 360 ++--- l_bsp_ent.h | 116 +- l_bsp_hl.c | 1776 ++++++++++----------- l_bsp_hl.h | 628 ++++---- l_bsp_q1.c | 1240 +++++++-------- l_bsp_q1.h | 550 +++---- l_bsp_q2.c | 2268 +++++++++++++-------------- l_bsp_q2.h | 196 +-- l_bsp_q3.c | 1648 ++++++++++---------- l_bsp_q3.h | 162 +- l_bsp_sin.c | 2372 ++++++++++++++-------------- l_bsp_sin.h | 212 +-- l_cmd.c | 2460 ++++++++++++++--------------- l_cmd.h | 314 ++-- l_log.c | 430 +++--- l_log.h | 84 +- l_math.c | 578 +++---- l_math.h | 186 +-- l_mem.c | 882 +++++------ l_mem.h | 102 +- l_poly.c | 2822 +++++++++++++++++----------------- l_poly.h | 240 +-- l_qfiles.c | 1326 ++++++++-------- l_qfiles.h | 182 +-- l_threads.c | 3020 ++++++++++++++++++------------------ l_threads.h | 90 +- l_utils.c | 518 +++---- l_utils.h | 158 +- lcc.mak | 122 +- leakfile.c | 202 +-- linux-i386.mak | 218 +-- map.c | 2534 +++++++++++++++--------------- map_hl.c | 2228 +++++++++++++-------------- map_q1.c | 2348 ++++++++++++++-------------- map_q2.c | 2324 ++++++++++++++-------------- map_q3.c | 1362 ++++++++--------- map_sin.c | 2422 ++++++++++++++--------------- nodraw.c | 94 +- portals.c | 2594 +++++++++++++++---------------- prtfile.c | 574 +++---- q2files.h | 974 ++++++------ q3files.h | 748 ++++----- qbsp.h | 954 ++++++------ qfiles.h | 974 ++++++------ sinfiles.h | 730 ++++----- tetrahedron.c | 2778 ++++++++++++++++----------------- tetrahedron.h | 48 +- textures.c | 456 +++--- tree.c | 566 +++---- writebsp.c | 1190 +++++++------- 84 files changed, 39039 insertions(+), 39039 deletions(-) diff --git a/Conscript b/Conscript index 08487ab..264e7c7 100644 --- a/Conscript +++ b/Conscript @@ -1,75 +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'; +# 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 index 2f16169..b6d3b94 100644 --- a/Makefile +++ b/Makefile @@ -1,114 +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" -# - +# +# 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/aas_areamerging.c b/aas_areamerging.c index c5f82d2..f9de65f 100644 --- a/aas_areamerging.c +++ b/aas_areamerging.c @@ -1,390 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index c0b39a4..14cb8ed 100644 --- a/aas_areamerging.h +++ b/aas_areamerging.h @@ -1,24 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 3aeb569..b805176 100644 --- a/aas_cfg.c +++ b/aas_cfg.c @@ -1,252 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 25537c6..a5db5a8 100644 --- a/aas_cfg.h +++ b/aas_cfg.h @@ -1,73 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 914b25b..1705362 100644 --- a/aas_create.c +++ b/aas_create.c @@ -1,1142 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index cc5693c..7f41f02 100644 --- a/aas_create.h +++ b/aas_create.h @@ -1,136 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index e84f48b..e59f4f9 100644 --- a/aas_edgemelting.c +++ b/aas_edgemelting.c @@ -1,108 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 4c03e97..a296cb6 100644 --- a/aas_edgemelting.h +++ b/aas_edgemelting.h @@ -1,24 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index bf170de..2fb2903 100644 --- a/aas_facemerging.c +++ b/aas_facemerging.c @@ -1,282 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 5a81735..b2e02e5 100644 --- a/aas_facemerging.h +++ b/aas_facemerging.h @@ -1,24 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 9f41639..9dc6b12 100644 --- a/aas_file.c +++ b/aas_file.c @@ -1,549 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 5176461..d3e15c2 100644 --- a/aas_file.h +++ b/aas_file.h @@ -1,25 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index d9ba597..4cfb194 100644 --- a/aas_gsubdiv.c +++ b/aas_gsubdiv.c @@ -1,656 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 0156a9a..bb2b095 100644 --- a/aas_gsubdiv.h +++ b/aas_gsubdiv.h @@ -1,25 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index da16d1b..10c616a 100644 --- a/aas_map.c +++ b/aas_map.c @@ -1,849 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 601cdfb..181f711 100644 --- a/aas_map.h +++ b/aas_map.h @@ -1,23 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index a03e9d2..1ba292a 100644 --- a/aas_prunenodes.c +++ b/aas_prunenodes.c @@ -1,89 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 36989ad..9433864 100644 --- a/aas_prunenodes.h +++ b/aas_prunenodes.h @@ -1,24 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 4836d51..d549ab7 100644 --- a/aas_store.c +++ b/aas_store.c @@ -1,1082 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 26957e4..063adbe 100644 --- a/aas_store.h +++ b/aas_store.h @@ -1,107 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 7ed8677..fc3dc77 100644 --- a/aasfile.h +++ b/aasfile.h @@ -1,252 +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 -*/ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 127549c..9473287 100644 --- a/be_aas_bspc.c +++ b/be_aas_bspc.c @@ -1,292 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 72e488a..97e2921 100644 --- a/be_aas_bspc.h +++ b/be_aas_bspc.h @@ -1,23 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 22cb0dd..d8accec 100644 --- a/brushbsp.c +++ b/brushbsp.c @@ -1,1871 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 4f58b53..e19076e 100644 --- a/bspc.c +++ b/bspc.c @@ -1,991 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 79dc8ae..b09fb52 100644 --- a/bspc.sln +++ b/bspc.sln @@ -1,28 +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 +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 index 6171e1a..0fb1bd7 100644 --- a/bspc.vcproj +++ b/bspc.vcproj @@ -1,1381 +1,1381 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cfgq3.c b/cfgq3.c index 588509c..c0598ae 100644 --- a/cfgq3.c +++ b/cfgq3.c @@ -1,84 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index c141075..fc7c327 100644 --- a/csg.c +++ b/csg.c @@ -1,1005 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index bcd3ef9..f05f5ae 100644 --- a/faces.c +++ b/faces.c @@ -1,978 +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); -} +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 9f7d6f7..18ed2c8 100644 --- a/gldraw.c +++ b/gldraw.c @@ -1,232 +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; -} +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 179a836..e32821b 100644 --- a/glfile.c +++ b/glfile.c @@ -1,149 +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); -} - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 8260179..0cc8f8e 100644 --- a/l_bsp_ent.c +++ b/l_bsp_ent.c @@ -1,180 +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; -} - - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 05dd162..461b91c 100644 --- a/l_bsp_ent.h +++ b/l_bsp_ent.h @@ -1,58 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 5b922b5..48cb7e8 100644 --- a/l_bsp_hl.c +++ b/l_bsp_hl.c @@ -1,888 +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; -} //*/ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 0089b1e..4146202 100644 --- a/l_bsp_hl.h +++ b/l_bsp_hl.h @@ -1,314 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index c68b5c8..7ac1553 100644 --- a/l_bsp_q1.c +++ b/l_bsp_q1.c @@ -1,620 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 1c2cd92..7cf459c 100644 --- a/l_bsp_q1.h +++ b/l_bsp_q1.h @@ -1,275 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index d5e9844..2d97579 100644 --- a/l_bsp_q2.c +++ b/l_bsp_q2.c @@ -1,1134 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 8d937a8..f0720df 100644 --- a/l_bsp_q2.h +++ b/l_bsp_q2.h @@ -1,98 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 19a3941..24bb766 100644 --- a/l_bsp_q3.c +++ b/l_bsp_q3.c @@ -1,824 +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 - - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 6ac175c..3573c87 100644 --- a/l_bsp_q3.h +++ b/l_bsp_q3.h @@ -1,81 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index de51528..2c3c223 100644 --- a/l_bsp_sin.c +++ b/l_bsp_sin.c @@ -1,1186 +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); -} +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 93cb69f..862674b 100644 --- a/l_bsp_sin.h +++ b/l_bsp_sin.h @@ -1,106 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 35fef7b..e35104b 100644 --- a/l_cmd.c +++ b/l_cmd.c @@ -1,1230 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index b5497b4..613cafa 100644 --- a/l_cmd.h +++ b/l_cmd.h @@ -1,157 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index b685ed3..6a69c2b 100644 --- a/l_log.c +++ b/l_log.c @@ -1,215 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index eff7bf6..b9f1be4 100644 --- a/l_log.h +++ b/l_log.h @@ -1,42 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index caf3419..8d26a05 100644 --- a/l_math.c +++ b/l_math.c @@ -1,289 +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; - } -} +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 1df7b61..4cd4a6c 100644 --- a/l_math.h +++ b/l_math.h @@ -1,93 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 831543e..31e02b2 100644 --- a/l_mem.c +++ b/l_mem.c @@ -1,441 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index d517743..ba3b0d3 100644 --- a/l_mem.h +++ b/l_mem.h @@ -1,51 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index fea2bef..7627d20 100644 --- a/l_poly.c +++ b/l_poly.c @@ -1,1411 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index a14b834..f3191eb 100644 --- a/l_poly.h +++ b/l_poly.h @@ -1,120 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 32cb6de..e2e8d10 100644 --- a/l_qfiles.c +++ b/l_qfiles.c @@ -1,663 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 308c608..0f79e7f 100644 --- a/l_qfiles.h +++ b/l_qfiles.h @@ -1,91 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index bc4b998..94e641a 100644 --- a/l_threads.c +++ b/l_threads.c @@ -1,1510 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 2c6d8df..fe4df17 100644 --- a/l_threads.h +++ b/l_threads.h @@ -1,45 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index f9d6d5f..8b229aa 100644 --- a/l_utils.c +++ b/l_utils.c @@ -1,259 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 2ef9161..ebd8dbb 100644 --- a/l_utils.h +++ b/l_utils.h @@ -1,79 +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 - - - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index d74adce..1b99864 100644 --- a/lcc.mak +++ b/lcc.mak @@ -1,61 +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) $< - +# +# 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 index 7118759..924b34d 100644 --- a/leakfile.c +++ b/leakfile.c @@ -1,101 +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); -} - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 00a261d..dd9566d 100644 --- a/linux-i386.mak +++ b/linux-i386.mak @@ -1,109 +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" -# - +# +# 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 index 613c234..e156d3a 100644 --- a/map.c +++ b/map.c @@ -1,1267 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 7e6037b..88e4fa7 100644 --- a/map_hl.c +++ b/map_hl.c @@ -1,1114 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 772a358..fa9d117 100644 --- a/map_q1.c +++ b/map_q1.c @@ -1,1174 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 0f15216..a91e78a 100644 --- a/map_q2.c +++ b/map_q2.c @@ -1,1162 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 750b304..375100c 100644 --- a/map_q3.c +++ b/map_q3.c @@ -1,681 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 7794b8c..b6e67ff 100644 --- a/map_sin.c +++ b/map_sin.c @@ -1,1211 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index b5442dc..7503a48 100644 --- a/nodraw.c +++ b/nodraw.c @@ -1,47 +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) -{ -} +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 3d3389d..cde4f60 100644 --- a/portals.c +++ b/portals.c @@ -1,1297 +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 - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 69834aa..58d1206 100644 --- a/prtfile.c +++ b/prtfile.c @@ -1,287 +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); -} - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 5ffea6e..317e47c 100644 --- a/q2files.h +++ b/q2files.h @@ -1,487 +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; +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index b251cc0..5ed267d 100644 --- a/q3files.h +++ b/q3files.h @@ -1,374 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 7ebf9d4..dd57200 100644 --- a/qbsp.h +++ b/qbsp.h @@ -1,477 +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); +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 5ffea6e..317e47c 100644 --- a/qfiles.h +++ b/qfiles.h @@ -1,487 +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; +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 31091c5..bcb5ee6 100644 --- a/sinfiles.h +++ b/sinfiles.h @@ -1,365 +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; +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 36c10af..4c4d251 100644 --- a/tetrahedron.c +++ b/tetrahedron.c @@ -1,1389 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 89003f1..2c6293d 100644 --- a/tetrahedron.h +++ b/tetrahedron.h @@ -1,24 +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); - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index ad6a8f5..76cc0fd 100644 --- a/textures.c +++ b/textures.c @@ -1,228 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 3d2ee9c..bc3cb8e 100644 --- a/tree.c +++ b/tree.c @@ -1,283 +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 +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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 index 38e9980..b6cf4e4 100644 --- a/writebsp.c +++ b/writebsp.c @@ -1,595 +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++; -} - +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General 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++; +} +