aboutsummaryrefslogtreecommitdiffstats
path: root/code/bspc/map.c
diff options
context:
space:
mode:
Diffstat (limited to 'code/bspc/map.c')
-rwxr-xr-xcode/bspc/map.c1267
1 files changed, 1267 insertions, 0 deletions
diff --git a/code/bspc/map.c b/code/bspc/map.c
new file mode 100755
index 0000000..613c234
--- /dev/null
+++ b/code/bspc/map.c
@@ -0,0 +1,1267 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+#include "l_bsp_hl.h"
+#include "l_bsp_q1.h"
+#include "l_bsp_q2.h"
+#include "l_bsp_q3.h"
+#include "l_bsp_sin.h"
+#include "l_mem.h"
+#include "../botlib/aasfile.h" //aas_bbox_t
+#include "aas_store.h" //AAS_MAX_BBOXES
+#include "aas_cfg.h"
+
+#define Sign(x) (x < 0 ? 1 : 0)
+
+int nummapbrushes;
+mapbrush_t mapbrushes[MAX_MAPFILE_BRUSHES];
+
+int nummapbrushsides;
+side_t brushsides[MAX_MAPFILE_BRUSHSIDES];
+brush_texture_t side_brushtextures[MAX_MAPFILE_BRUSHSIDES];
+
+int nummapplanes;
+plane_t mapplanes[MAX_MAPFILE_PLANES];
+int mapplaneusers[MAX_MAPFILE_PLANES];
+
+#define PLANE_HASHES 1024
+plane_t *planehash[PLANE_HASHES];
+vec3_t map_mins, map_maxs;
+
+#ifdef SIN
+textureref_t side_newrefs[MAX_MAPFILE_BRUSHSIDES];
+#endif
+
+map_texinfo_t map_texinfo[MAX_MAPFILE_TEXINFO];
+int map_numtexinfo;
+int loadedmaptype; //loaded map type
+
+// undefine to make plane finding use linear sort
+#define USE_HASHING
+
+int c_boxbevels;
+int c_edgebevels;
+int c_areaportals;
+int c_clipbrushes;
+int c_squattbrushes;
+int c_writtenbrushes;
+
+/*
+=============================================================================
+
+PLANE FINDING
+
+=============================================================================
+*/
+
+
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+int PlaneSignBits(vec3_t normal)
+{
+ int i, signbits;
+
+ signbits = 0;
+ for (i = 2; i >= 0; i--)
+ {
+ signbits = (signbits << 1) + Sign(normal[i]);
+ } //end for
+ return signbits;
+} //end of the function PlaneSignBits
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+int PlaneTypeForNormal(vec3_t normal)
+{
+ vec_t ax, ay, az;
+
+// NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+} //end of the function PlaneTypeForNormal
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+//ME NOTE: changed from 0.00001
+#define NORMAL_EPSILON 0.0001
+//ME NOTE: changed from 0.01
+#define DIST_EPSILON 0.02
+qboolean PlaneEqual(plane_t *p, vec3_t normal, vec_t dist)
+{
+#if 1
+ if (
+ fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
+ && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
+ && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
+ && fabs(p->dist - dist) < DIST_EPSILON )
+ return true;
+#else
+ if (p->normal[0] == normal[0]
+ && p->normal[1] == normal[1]
+ && p->normal[2] == normal[2]
+ && p->dist == dist)
+ return true;
+#endif
+ return false;
+} //end of the function PlaneEqual
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void AddPlaneToHash(plane_t *p)
+{
+ int hash;
+
+ hash = (int)fabs(p->dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ p->hash_chain = planehash[hash];
+ planehash[hash] = p;
+} //end of the function AddPlaneToHash
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+int CreateNewFloatPlane (vec3_t normal, vec_t dist)
+{
+ plane_t *p, temp;
+
+ if (VectorLength(normal) < 0.5)
+ Error ("FloatPlane: bad normal");
+ // create a new plane
+ if (nummapplanes+2 > MAX_MAPFILE_PLANES)
+ Error ("MAX_MAPFILE_PLANES");
+
+ p = &mapplanes[nummapplanes];
+ VectorCopy (normal, p->normal);
+ p->dist = dist;
+ p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+ p->signbits = PlaneSignBits(p->normal);
+
+ VectorSubtract (vec3_origin, normal, (p+1)->normal);
+ (p+1)->dist = -dist;
+ (p+1)->signbits = PlaneSignBits((p+1)->normal);
+
+ nummapplanes += 2;
+
+ // allways put axial planes facing positive first
+ if (p->type < 3)
+ {
+ if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+ {
+ // flip order
+ temp = *p;
+ *p = *(p+1);
+ *(p+1) = temp;
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 1;
+ }
+ }
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 2;
+} //end of the function CreateNewFloatPlane
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void SnapVector(vec3_t normal)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ break;
+ }
+ if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = -1;
+ break;
+ }
+ }
+} //end of the function SnapVector
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void SnapPlane(vec3_t normal, vec_t *dist)
+{
+ SnapVector(normal);
+
+ if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
+ *dist = Q_rint(*dist);
+} //end of the function SnapPlane
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+#ifndef USE_HASHING
+int FindFloatPlane(vec3_t normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+
+ SnapPlane(normal, &dist);
+ for (i = 0, p = mapplanes; i < nummapplanes; i++, p++)
+ {
+ if (PlaneEqual (p, normal, dist))
+ {
+ mapplaneusers[i]++;
+ return i;
+ } //end if
+ } //end for
+ i = CreateNewFloatPlane (normal, dist);
+ mapplaneusers[i]++;
+ return i;
+} //end of the function FindFloatPlane
+#else
+int FindFloatPlane (vec3_t normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+ int hash, h;
+
+ SnapPlane (normal, &dist);
+ hash = (int)fabs(dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ // search the border bins as well
+ for (i = -1; i <= 1; i++)
+ {
+ h = (hash+i)&(PLANE_HASHES-1);
+ for (p = planehash[h]; p; p = p->hash_chain)
+ {
+ if (PlaneEqual(p, normal, dist))
+ {
+ mapplaneusers[p-mapplanes]++;
+ return p - mapplanes;
+ } //end if
+ } //end for
+ } //end for
+ i = CreateNewFloatPlane (normal, dist);
+ mapplaneusers[i]++;
+ return i;
+} //end of the function FindFloatPlane
+#endif
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+int PlaneFromPoints (int *p0, int *p1, int *p2)
+{
+ vec3_t t1, t2, normal;
+ vec_t dist;
+
+ VectorSubtract (p0, p1, t1);
+ VectorSubtract (p2, p1, t2);
+ CrossProduct (t1, t2, normal);
+ VectorNormalize (normal);
+
+ dist = DotProduct (p0, normal);
+
+ return FindFloatPlane (normal, dist);
+} //end of the function PlaneFromPoints
+//===========================================================================
+// Adds any additional planes necessary to allow the brush to be expanded
+// against axial bounding boxes
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void AddBrushBevels (mapbrush_t *b)
+{
+ int axis, dir;
+ int i, j, k, l, order;
+ side_t sidetemp;
+ brush_texture_t tdtemp;
+#ifdef SIN
+ textureref_t trtemp;
+#endif
+ side_t *s, *s2;
+ vec3_t normal;
+ float dist;
+ winding_t *w, *w2;
+ vec3_t vec, vec2;
+ float d;
+
+ //
+ // add the axial planes
+ //
+ order = 0;
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2, order++)
+ {
+ // see if the plane is allready present
+ for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
+ {
+ if (mapplanes[s->planenum].normal[axis] == dir)
+ break;
+ }
+
+ if (i == b->numsides)
+ { // add a new side
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ b->numsides++;
+ VectorClear (normal);
+ normal[axis] = dir;
+ if (dir == 1)
+ dist = b->maxs[axis];
+ else
+ dist = -b->mins[axis];
+ s->planenum = FindFloatPlane (normal, dist);
+ s->texinfo = b->original_sides[0].texinfo;
+#ifdef SIN
+ s->lightinfo = b->original_sides[0].lightinfo;
+#endif
+ s->contents = b->original_sides[0].contents;
+ s->flags |= SFL_BEVEL;
+ c_boxbevels++;
+ }
+
+ // if the plane is not in it canonical order, swap it
+ if (i != order)
+ {
+ sidetemp = b->original_sides[order];
+ b->original_sides[order] = b->original_sides[i];
+ b->original_sides[i] = sidetemp;
+
+ j = b->original_sides - brushsides;
+ tdtemp = side_brushtextures[j+order];
+ side_brushtextures[j+order] = side_brushtextures[j+i];
+ side_brushtextures[j+i] = tdtemp;
+
+#ifdef SIN
+ trtemp = side_newrefs[j+order];
+ side_newrefs[j+order] = side_newrefs[j+i];
+ side_newrefs[j+i] = trtemp;
+#endif
+ }
+ }
+ }
+
+ //
+ // add the edge bevels
+ //
+ if (b->numsides == 6)
+ return; // pure axial
+
+ // test the non-axial plane edges
+ for (i=6 ; i<b->numsides ; i++)
+ {
+ s = b->original_sides + i;
+ w = s->winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ k = (j+1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[k], vec);
+ if (VectorNormalize (vec) < 0.5)
+ continue;
+ SnapVector (vec);
+ for (k=0 ; k<3 ; k++)
+ if ( vec[k] == -1 || vec[k] == 1)
+ break; // axial
+ if (k != 3)
+ continue; // only test non-axial edges
+
+ // try the six possible slanted axials from this edge
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2)
+ {
+ // construct a plane
+ VectorClear (vec2);
+ vec2[axis] = dir;
+ CrossProduct (vec, vec2, normal);
+ if (VectorNormalize (normal) < 0.5)
+ continue;
+ dist = DotProduct (w->p[j], normal);
+
+ // if all the points on all the sides are
+ // behind this plane, it is a proper edge bevel
+ for (k=0 ; k<b->numsides ; k++)
+ {
+ // if this plane has allready been used, skip it
+ if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]
+ , normal, dist) )
+ break;
+
+ w2 = b->original_sides[k].winding;
+ if (!w2)
+ continue;
+ for (l=0 ; l<w2->numpoints ; l++)
+ {
+ d = DotProduct (w2->p[l], normal) - dist;
+ if (d > 0.1)
+ break; // point in front
+ }
+ if (l != w2->numpoints)
+ break;
+ }
+
+ if (k != b->numsides)
+ continue; // wasn't part of the outer hull
+ // add this plane
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ s2 = &b->original_sides[b->numsides];
+ s2->planenum = FindFloatPlane (normal, dist);
+ s2->texinfo = b->original_sides[0].texinfo;
+#ifdef SIN
+ s2->lightinfo = b->original_sides[0].lightinfo;
+#endif
+ s2->contents = b->original_sides[0].contents;
+ s2->flags |= SFL_BEVEL;
+ c_edgebevels++;
+ b->numsides++;
+ }
+ }
+ }
+ }
+} //end of the function AddBrushBevels
+//===========================================================================
+// creates windigs for sides and mins / maxs for the brush
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+qboolean MakeBrushWindings(mapbrush_t *ob)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ ClearBounds (ob->mins, ob->maxs);
+
+ for (i = 0; i < ob->numsides; i++)
+ {
+ plane = &mapplanes[ob->original_sides[i].planenum];
+ w = BaseWindingForPlane(plane->normal, plane->dist);
+ for (j = 0; j <ob->numsides && w; j++)
+ {
+ if (i == j) continue;
+ if (ob->original_sides[j].flags & SFL_BEVEL) continue;
+ plane = &mapplanes[ob->original_sides[j].planenum^1];
+ ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+ }
+
+ side = &ob->original_sides[i];
+ side->winding = w;
+ if (w)
+ {
+ side->flags |= SFL_VISIBLE;
+ for (j = 0; j < w->numpoints; j++)
+ AddPointToBounds (w->p[j], ob->mins, ob->maxs);
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ //IDBUG: all the indexes into the mins and maxs were zero (not using i)
+ if (ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS)
+ {
+ Log_Print("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);
+ ob->numsides = 0; //remove the brush
+ break;
+ } //end if
+ if (ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS)
+ {
+ Log_Print("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);
+ ob->numsides = 0; //remove the brush
+ break;
+ } //end if
+ } //end for
+ return true;
+} //end of the function MakeBrushWindings
+//===========================================================================
+// FIXME: currently doesn't mark all bevels
+// NOTE: when one brush bevel is found the remaining sides of the brush
+// are bevels as well (when the brush isn't expanded for AAS :))
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void MarkBrushBevels(mapbrush_t *brush)
+{
+ int i;
+ int we;
+ side_t *s;
+
+ //check all the sides of the brush
+ for (i = 0; i < brush->numsides; i++)
+ {
+ s = brush->original_sides + i;
+ //if the side has no winding
+ if (!s->winding)
+ {
+ Log_Write("MarkBrushBevels: brush %d no winding", brush->brushnum);
+ s->flags |= SFL_BEVEL;
+ } //end if
+ //if the winding is tiny
+ else if (WindingIsTiny(s->winding))
+ {
+ s->flags |= SFL_BEVEL;
+ Log_Write("MarkBrushBevels: brush %d tiny winding", brush->brushnum);
+ } //end else if
+ //if the winding has errors
+ else
+ {
+ we = WindingError(s->winding);
+ if (we == WE_NOTENOUGHPOINTS
+ || we == WE_SMALLAREA
+ || we == WE_POINTBOGUSRANGE
+// || we == WE_NONCONVEX
+ )
+ {
+ Log_Write("MarkBrushBevels: brush %d %s", brush->brushnum, WindingErrorString());
+ s->flags |= SFL_BEVEL;
+ } //end else if
+ } //end else
+ if (s->flags & SFL_BEVEL)
+ {
+ s->flags &= ~SFL_VISIBLE;
+ //if the side has a valid plane
+ if (s->planenum > 0 && s->planenum < nummapplanes)
+ {
+ //if it is an axial plane
+ if (mapplanes[s->planenum].type < 3) c_boxbevels++;
+ else c_edgebevels++;
+ } //end if
+ } //end if
+ } //end for
+} //end of the function MarkBrushBevels
+//===========================================================================
+// returns true if the map brush already exists
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+int BrushExists(mapbrush_t *brush)
+{
+ int i, s1, s2;
+ side_t *side1, *side2;
+ mapbrush_t *brush1, *brush2;
+
+ for (i = 0; i < nummapbrushes; i++)
+ {
+ brush1 = brush;
+ brush2 = &mapbrushes[i];
+ //compare the brushes
+ if (brush1->entitynum != brush2->entitynum) continue;
+ //if (brush1->contents != brush2->contents) continue;
+ if (brush1->numsides != brush2->numsides) continue;
+ for (s1 = 0; s1 < brush1->numsides; s1++)
+ {
+ side1 = brush1->original_sides + s1;
+ //
+ for (s2 = 0; s2 < brush2->numsides; s2++)
+ {
+ side2 = brush2->original_sides + s2;
+ //
+ if ((side1->planenum & ~1) == (side2->planenum & ~1)
+// && side1->texinfo == side2->texinfo
+// && side1->contents == side2->contents
+// && side1->surf == side2->surf
+ ) break;
+ } //end if
+ if (s2 >= brush2->numsides) break;
+ } //end for
+ if (s1 >= brush1->numsides) return true;
+ } //end for
+ return false;
+} //end of the function BrushExists
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+qboolean WriteMapBrush(FILE *fp, mapbrush_t *brush, vec3_t origin)
+{
+ int sn, rotate, shift[2], sv, tv, planenum, p1, i, j;
+ float scale[2], originshift[2], ang1, ang2, newdist;
+ vec3_t vecs[2], axis[2];
+ map_texinfo_t *ti;
+ winding_t *w;
+ side_t *s;
+ plane_t *plane;
+
+ if (noliquids)
+ {
+ if (brush->contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))
+ {
+ return true;
+ } //end if
+ } //end if
+ //if the brush has no contents
+ if (!brush->contents) return true;
+ //print the leading {
+ if (fprintf(fp, " { //brush %d\n", brush->brushnum) < 0) return false;
+ //write brush sides
+ for (sn = 0; sn < brush->numsides; sn++)
+ {
+ s = brush->original_sides + sn;
+ //don't write out bevels
+ if (!(s->flags & SFL_BEVEL))
+ {
+ //if the entity has an origin set
+ if (origin[0] || origin[1] || origin[2])
+ {
+ newdist = mapplanes[s->planenum].dist +
+ DotProduct(mapplanes[s->planenum].normal, origin);
+ planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist);
+ } //end if
+ else
+ {
+ planenum = s->planenum;
+ } //end else
+ //always take the first plane, then flip the points if necesary
+ plane = &mapplanes[planenum & ~1];
+ w = BaseWindingForPlane(plane->normal, plane->dist);
+ //
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (fabs(w->p[i][j]) < 0.2) w->p[i][j] = 0;
+ else if (fabs((int)w->p[i][j] - w->p[i][j]) < 0.3) w->p[i][j] = (int) w->p[i][j];
+ //w->p[i][j] = (int) (w->p[i][j] + 0.2);
+ } //end for
+ } //end for
+ //three non-colinear points to define the plane
+ if (planenum & 1) p1 = 1;
+ else p1 = 0;
+ if (fprintf(fp," ( %5i %5i %5i ) ", (int)w->p[p1][0], (int)w->p[p1][1], (int)w->p[p1][2]) < 0) return false;
+ if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[!p1][0], (int)w->p[!p1][1], (int)w->p[!p1][2]) < 0) return false;
+ if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false;
+ //free the winding
+ FreeWinding(w);
+ //
+ if (s->texinfo == TEXINFO_NODE)
+ {
+ if (brush->contents & CONTENTS_PLAYERCLIP)
+ {
+ //player clip
+ if (loadedmaptype == MAPTYPE_SIN)
+ {
+ if (fprintf(fp, "generic/misc/clip 0 0 0 1 1") < 0) return false;
+ } //end if
+ else if (loadedmaptype == MAPTYPE_QUAKE2)
+ { //FIXME: don't always use e1u1
+ if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false;
+ } //end else
+ else if (loadedmaptype == MAPTYPE_QUAKE3)
+ {
+ if (fprintf(fp, "e1u1/clip 0 0 0 1 1") < 0) return false;
+ } //end else if
+ else
+ {
+ if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
+ } //end else
+ } //end if
+ else if (brush->contents == CONTENTS_MONSTERCLIP)
+ {
+ //monster clip
+ if (loadedmaptype == MAPTYPE_SIN)
+ {
+ if (fprintf(fp, "generic/misc/monster 0 0 0 1 1") < 0) return false;
+ } //end if
+ else if (loadedmaptype == MAPTYPE_QUAKE2)
+ {
+ if (fprintf(fp, "e1u1/clip_mon 0 0 0 1 1") < 0) return false;
+ } //end else
+ else
+ {
+ if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
+ } //end else
+ } //end else
+ else
+ {
+ if (fprintf(fp, "clip 0 0 0 1 1") < 0) return false;
+ Log_Write("brush->contents = %d\n", brush->contents);
+ } //end else
+ } //end if
+ else if (loadedmaptype == MAPTYPE_SIN && s->texinfo == 0)
+ {
+ if (brush->contents & CONTENTS_DUMMYFENCE)
+ {
+ if (fprintf(fp, "generic/misc/fence 0 0 0 1 1") < 0) return false;
+ } //end if
+ else if (brush->contents & CONTENTS_MIST)
+ {
+ if (fprintf(fp, "generic/misc/volumetric_base 0 0 0 1 1") < 0) return false;
+ } //end if
+ else //unknown so far
+ {
+ if (fprintf(fp, "generic/misc/red 0 0 0 1 1") < 0) return false;
+ } //end else
+ } //end if
+ else if (loadedmaptype == MAPTYPE_QUAKE3)
+ {
+ //always use the same texture
+ if (fprintf(fp, "e2u3/floor1_2 0 0 0 1 1 1 0 0") < 0) return false;
+ } //end else if
+ else
+ {
+ //*
+ ti = &map_texinfo[s->texinfo];
+ //the scaling of the texture
+ scale[0] = 1 / VectorNormalize2(ti->vecs[0], vecs[0]);
+ scale[1] = 1 / VectorNormalize2(ti->vecs[1], vecs[1]);
+ //
+ TextureAxisFromPlane(plane, axis[0], axis[1]);
+ //calculate texture shift done by entity origin
+ originshift[0] = DotProduct(origin, axis[0]);
+ originshift[1] = DotProduct(origin, axis[1]);
+ //the texture shift without origin shift
+ shift[0] = ti->vecs[0][3] - originshift[0];
+ shift[1] = ti->vecs[1][3] - originshift[1];
+ //
+ if (axis[0][0]) sv = 0;
+ else if (axis[0][1]) sv = 1;
+ else sv = 2;
+ if (axis[1][0]) tv = 0;
+ else if (axis[1][1]) tv = 1;
+ else tv = 2;
+ //calculate rotation of texture
+ if (vecs[0][tv] == 0) ang1 = vecs[0][sv] > 0 ? 90.0 : -90.0;
+ else ang1 = atan2(vecs[0][sv], vecs[0][tv]) * 180 / Q_PI;
+ if (ang1 < 0) ang1 += 360;
+ if (ang1 >= 360) ang1 -= 360;
+ if (axis[0][tv] == 0) ang2 = axis[0][sv] > 0 ? 90.0 : -90.0;
+ else ang2 = atan2(axis[0][sv], axis[0][tv]) * 180 / Q_PI;
+ if (ang2 < 0) ang2 += 360;
+ if (ang2 >= 360) ang2 -= 360;
+ rotate = ang2 - ang1;
+ if (rotate < 0) rotate += 360;
+ if (rotate >= 360) rotate -= 360;
+ //write the texture info
+ if (fprintf(fp, "%s %d %d %d", ti->texture, shift[0], shift[1], rotate) < 0) return false;
+ if (fabs(scale[0] - ((int) scale[0])) < 0.001)
+ {
+ if (fprintf(fp, " %d", (int) scale[0]) < 0) return false;
+ } //end if
+ else
+ {
+ if (fprintf(fp, " %4f", scale[0]) < 0) return false;
+ } //end if
+ if (fabs(scale[1] - ((int) scale[1])) < 0.001)
+ {
+ if (fprintf(fp, " %d", (int) scale[1]) < 0) return false;
+ } //end if
+ else
+ {
+ if (fprintf(fp, " %4f", scale[1]) < 0) return false;
+ } //end else
+ //write the extra brush side info
+ if (loadedmaptype == MAPTYPE_QUAKE2)
+ {
+ if (fprintf(fp, " %ld %ld %ld", s->contents, ti->flags, ti->value) < 0) return false;
+ } //end if
+ //*/
+ } //end else
+ if (fprintf(fp, "\n") < 0) return false;
+ } //end if
+ } //end if
+ if (fprintf(fp, " }\n") < 0) return false;
+ c_writtenbrushes++;
+ return true;
+} //end of the function WriteMapBrush
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+qboolean WriteOriginBrush(FILE *fp, vec3_t origin)
+{
+ vec3_t normal;
+ float dist;
+ int i, s;
+ winding_t *w;
+
+ if (fprintf(fp, " {\n") < 0) return false;
+ //
+ for (i = 0; i < 3; i++)
+ {
+ for (s = -1; s <= 1; s += 2)
+ {
+ //
+ VectorClear(normal);
+ normal[i] = s;
+ dist = origin[i] * s + 16;
+ //
+ w = BaseWindingForPlane(normal, dist);
+ //three non-colinear points to define the plane
+ if (fprintf(fp," ( %5i %5i %5i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]) < 0) return false;
+ if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]) < 0) return false;
+ if (fprintf(fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]) < 0) return false;
+ //free the winding
+ FreeWinding(w);
+ //write origin texture:
+ // CONTENTS_ORIGIN = 16777216
+ // SURF_NODRAW = 128
+ if (loadedmaptype == MAPTYPE_SIN)
+ {
+ if (fprintf(fp, "generic/misc/origin 0 0 0 1 1") < 0) return false;
+ } //end if
+ else if (loadedmaptype == MAPTYPE_HALFLIFE)
+ {
+ if (fprintf(fp, "origin 0 0 0 1 1") < 0) return false;
+ } //end if
+ else
+ {
+ if (fprintf(fp, "e1u1/origin 0 0 0 1 1") < 0) return false;
+ } //end else
+ //Quake2 extra brush side info
+ if (loadedmaptype == MAPTYPE_QUAKE2)
+ {
+ //if (fprintf(fp, " 16777216 128 0") < 0) return false;
+ } //end if
+ if (fprintf(fp, "\n") < 0) return false;
+ } //end for
+ } //end for
+ if (fprintf(fp, " }\n") < 0) return false;
+ c_writtenbrushes++;
+ return true;
+} //end of the function WriteOriginBrush
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+mapbrush_t *GetAreaPortalBrush(entity_t *mapent)
+{
+ int portalnum, bn;
+ mapbrush_t *brush;
+
+ //the area portal number
+ portalnum = mapent->areaportalnum;
+ //find the area portal brush in the world brushes
+ for (bn = 0; bn < nummapbrushes && portalnum; bn++)
+ {
+ brush = &mapbrushes[bn];
+ //must be in world entity
+ if (brush->entitynum == 0)
+ {
+ if (brush->contents & CONTENTS_AREAPORTAL)
+ {
+ portalnum--;
+ } //end if
+ } //end if
+ } //end for
+ if (bn < nummapbrushes)
+ {
+ return brush;
+ } //end if
+ else
+ {
+ Log_Print("area portal %d brush not found\n", mapent->areaportalnum);
+ return NULL;
+ } //end else
+} //end of the function GetAreaPortalBrush
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+qboolean WriteMapFileSafe(FILE *fp)
+{
+ char key[1024], value[1024];
+ int i, bn, entitybrushes;
+ epair_t *ep;
+ mapbrush_t *brush;
+ entity_t *mapent;
+ //vec3_t vec_origin = {0, 0, 0};
+
+ //
+ if (fprintf(fp,"//=====================================================\n"
+ "//\n"
+ "// map file created with BSPC "BSPC_VERSION"\n"
+ "//\n"
+ "// BSPC is designed to decompile material in which you own the copyright\n"
+ "// or have obtained permission to decompile from the copyright owner. Unless\n"
+ "// you own the copyright or have permission to decompile from the copyright\n"
+ "// owner, you may be violating copyright law and be subject to payment of\n"
+ "// damages and other remedies. If you are uncertain about your rights, contact\n"
+ "// your legal advisor.\n"
+ "//\n") < 0) return false;
+ if (loadedmaptype == MAPTYPE_SIN)
+ {
+ if (fprintf(fp,
+ "// generic/misc/red is used for unknown textures\n") < 0) return false;
+ } //end if
+ if (fprintf(fp,"//\n"
+ "//=====================================================\n") < 0) return false;
+ //write out all the entities
+ for (i = 0; i < num_entities; i++)
+ {
+ mapent = &entities[i];
+ if (!mapent->epairs)
+ {
+ continue;
+ } //end if
+ if (fprintf(fp, "{\n") < 0) return false;
+ //
+ if (loadedmaptype == MAPTYPE_QUAKE3)
+ {
+ if (!stricmp(ValueForKey(mapent, "classname"), "light"))
+ {
+ SetKeyValue(mapent, "light", "10000");
+ } //end if
+ } //end if
+ //write epairs
+ for (ep = mapent->epairs; ep; ep = ep->next)
+ {
+ strcpy(key, ep->key);
+ StripTrailing (key);
+ strcpy(value, ep->value);
+ StripTrailing(value);
+ //
+ if (loadedmaptype == MAPTYPE_QUAKE2 ||
+ loadedmaptype == MAPTYPE_SIN)
+ {
+ //don't write an origin for BSP models
+ if (mapent->modelnum >= 0 && !strcmp(key, "origin")) continue;
+ } //end if
+ //don't write BSP model numbers
+ if (mapent->modelnum >= 0 && !strcmp(key, "model") && value[0] == '*') continue;
+ //
+ if (fprintf(fp, " \"%s\" \"%s\"\n", key, value) < 0) return false;
+ } //end for
+ //
+ if (ValueForKey(mapent, "origin")) GetVectorForKey(mapent, "origin", mapent->origin);
+ else mapent->origin[0] = mapent->origin[1] = mapent->origin[2] = 0;
+ //if this is an area portal entity
+ if (!strcmp("func_areaportal", ValueForKey(mapent, "classname")))
+ {
+ brush = GetAreaPortalBrush(mapent);
+ if (!brush) return false;
+ if (!WriteMapBrush(fp, brush, mapent->origin)) return false;
+ } //end if
+ else
+ {
+ entitybrushes = false;
+ //write brushes
+ for (bn = 0; bn < nummapbrushes; bn++)
+ {
+ brush = &mapbrushes[bn];
+ //if the brush is part of this entity
+ if (brush->entitynum == i)
+ {
+ //don't write out area portal brushes in the world
+ if (!((brush->contents & CONTENTS_AREAPORTAL) && brush->entitynum == 0))
+ {
+ /*
+ if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname")))
+ {
+ AAS_PositionFuncRotatingBrush(mapent, brush);
+ if (!WriteMapBrush(fp, brush, vec_origin)) return false;
+ } //end if
+ else //*/
+ {
+ if (!WriteMapBrush(fp, brush, mapent->origin)) return false;
+ } //end else
+ entitybrushes = true;
+ } //end if
+ } //end if
+ } //end for
+ //if the entity had brushes
+ if (entitybrushes)
+ {
+ //if the entity has an origin set
+ if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
+ {
+ if (!WriteOriginBrush(fp, mapent->origin)) return false;
+ } //end if
+ } //end if
+ } //end else
+ if (fprintf(fp, "}\n") < 0) return false;
+ } //end for
+ if (fprintf(fp, "//total of %d brushes\n", c_writtenbrushes) < 0) return false;
+ return true;
+} //end of the function WriteMapFileSafe
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void WriteMapFile(char *filename)
+{
+ FILE *fp;
+ double start_time;
+
+ c_writtenbrushes = 0;
+ //the time started
+ start_time = I_FloatTime();
+ //
+ Log_Print("writing %s\n", filename);
+ fp = fopen(filename, "wb");
+ if (!fp)
+ {
+ Log_Print("can't open %s\n", filename);
+ return;
+ } //end if
+ if (!WriteMapFileSafe(fp))
+ {
+ fclose(fp);
+ Log_Print("error writing map file %s\n", filename);
+ return;
+ } //end if
+ fclose(fp);
+ //display creation time
+ Log_Print("written %d brushes\n", c_writtenbrushes);
+ Log_Print("map file written in %5.0f seconds\n", I_FloatTime() - start_time);
+} //end of the function WriteMapFile
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void PrintMapInfo(void)
+{
+ Log_Print("\n");
+ Log_Print("%6i brushes\n", nummapbrushes);
+ Log_Print("%6i brush sides\n", nummapbrushsides);
+// Log_Print("%6i clipbrushes\n", c_clipbrushes);
+// Log_Print("%6i total sides\n", nummapbrushsides);
+// Log_Print("%6i boxbevels\n", c_boxbevels);
+// Log_Print("%6i edgebevels\n", c_edgebevels);
+// Log_Print("%6i entities\n", num_entities);
+// Log_Print("%6i planes\n", nummapplanes);
+// Log_Print("%6i areaportals\n", c_areaportals);
+// Log_Print("%6i squatt brushes\n", c_squattbrushes);
+// Log_Print("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
+// map_maxs[0],map_maxs[1],map_maxs[2]);
+} //end of the function PrintMapInfo
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+void ResetMapLoading(void)
+{
+ int i;
+ epair_t *ep, *nextep;
+
+ Q2_ResetMapLoading();
+ Sin_ResetMapLoading();
+
+ //free all map brush side windings
+ for (i = 0; i < nummapbrushsides; i++)
+ {
+ if (brushsides[i].winding)
+ {
+ FreeWinding(brushsides[i].winding);
+ } //end for
+ } //end for
+
+ //reset regular stuff
+ nummapbrushes = 0;
+ memset(mapbrushes, 0, MAX_MAPFILE_BRUSHES * sizeof(mapbrush_t));
+ //
+ nummapbrushsides = 0;
+ memset(brushsides, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(side_t));
+ memset(side_brushtextures, 0, MAX_MAPFILE_BRUSHSIDES * sizeof(brush_texture_t));
+ //
+ nummapplanes = 0;
+ memset(mapplanes, 0, MAX_MAPFILE_PLANES * sizeof(plane_t));
+ //
+ memset(planehash, 0, PLANE_HASHES * sizeof(plane_t *));
+ //
+ memset(map_texinfo, 0, MAX_MAPFILE_TEXINFO * sizeof(map_texinfo_t));
+ map_numtexinfo = 0;
+ //
+ VectorClear(map_mins);
+ VectorClear(map_maxs);
+ //
+ c_boxbevels = 0;
+ c_edgebevels = 0;
+ c_areaportals = 0;
+ c_clipbrushes = 0;
+ c_writtenbrushes = 0;
+ //clear the entities
+ for (i = 0; i < num_entities; i++)
+ {
+ for (ep = entities[i].epairs; ep; ep = nextep)
+ {
+ nextep = ep->next;
+ FreeMemory(ep->key);
+ FreeMemory(ep->value);
+ FreeMemory(ep);
+ } //end for
+ } //end for
+ num_entities = 0;
+ memset(entities, 0, MAX_MAP_ENTITIES * sizeof(entity_t));
+} //end of the function ResetMapLoading
+//===========================================================================
+//
+// Parameter: -
+// Returns: -
+// Changes Globals: -
+//===========================================================================
+#ifndef Q1_BSPVERSION
+#define Q1_BSPVERSION 29
+#endif
+#ifndef HL_BSPVERSION
+#define HL_BSPVERSION 30
+#endif
+
+#define Q2_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') //IBSP
+#define Q2_BSPVERSION 38
+
+#define SINGAME_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'R') //RBSP
+#define SINGAME_BSPVERSION 1
+
+#define SIN_BSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') //IBSP
+#define SIN_BSPVERSION 41
+
+typedef struct
+{
+ int ident;
+ int version;
+} idheader_t;
+
+int LoadMapFromBSP(struct quakefile_s *qf)
+{
+ idheader_t idheader;
+
+ if (ReadQuakeFile(qf, &idheader, 0, sizeof(idheader_t)) != sizeof(idheader_t))
+ {
+ return false;
+ } //end if
+
+ idheader.ident = LittleLong(idheader.ident);
+ idheader.version = LittleLong(idheader.version);
+ //Quake3 BSP file
+ if (idheader.ident == Q3_BSP_IDENT && idheader.version == Q3_BSP_VERSION)
+ {
+ ResetMapLoading();
+ Q3_LoadMapFromBSP(qf);
+ Q3_FreeMaxBSP();
+ } //end if
+ //Quake2 BSP file
+ else if (idheader.ident == Q2_BSPHEADER && idheader.version == Q2_BSPVERSION)
+ {
+ ResetMapLoading();
+ Q2_AllocMaxBSP();
+ Q2_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
+ Q2_FreeMaxBSP();
+ } //endif
+ //Sin BSP file
+ else if ((idheader.ident == SIN_BSPHEADER && idheader.version == SIN_BSPVERSION) ||
+ //the dorks gave the same format another ident and verions
+ (idheader.ident == SINGAME_BSPHEADER && idheader.version == SINGAME_BSPVERSION))
+ {
+ ResetMapLoading();
+ Sin_AllocMaxBSP();
+ Sin_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
+ Sin_FreeMaxBSP();
+ } //end if
+ //the Quake1 bsp files don't have a ident only a version
+ else if (idheader.ident == Q1_BSPVERSION)
+ {
+ ResetMapLoading();
+ Q1_AllocMaxBSP();
+ Q1_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
+ Q1_FreeMaxBSP();
+ } //end if
+ //Half-Life also only uses a version number
+ else if (idheader.ident == HL_BSPVERSION)
+ {
+ ResetMapLoading();
+ HL_AllocMaxBSP();
+ HL_LoadMapFromBSP(qf->filename, qf->offset, qf->length);
+ HL_FreeMaxBSP();
+ } //end if
+ else
+ {
+ Error("unknown BSP format %c%c%c%c, version %d\n",
+ (idheader.ident & 0xFF),
+ ((idheader.ident >> 8) & 0xFF),
+ ((idheader.ident >> 16) & 0xFF),
+ ((idheader.ident >> 24) & 0xFF), idheader.version);
+ return false;
+ } //end if
+ //
+ return true;
+} //end of the function LoadMapFromBSP