mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-14 00:40:39 +00:00
05e8ab9538
* Updated TODO * Moved ChangeLog to root * Updated ChangeLog * s/Foobar/Quake III Arena Source Code/ * Biggest patch EVAR. I wonder how many mail boxes this will fill...
1267 lines
35 KiB
C
1267 lines
35 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; 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
|