ioq3quest/q3map/map.c
2005-08-28 17:54:51 +00:00

1251 lines
26 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// map.c
#include "qbsp.h"
int entitySourceBrushes; // to track editor brush numbers
int numMapPatches;
// undefine to make plane finding use linear sort
#define USE_HASHING
#define PLANE_HASHES 1024
plane_t *planehash[PLANE_HASHES];
plane_t mapplanes[MAX_MAP_PLANES];
int nummapplanes;
// as brushes and patches are read in, the shaders are stored out in order
// here, so -onlytextures can just copy them out over the existing shaders
// in the drawSurfaces
char mapIndexedShaders[MAX_MAP_BRUSHSIDES][MAX_QPATH];
int numMapIndexedShaders;
vec3_t map_mins, map_maxs;
entity_t *mapent;
int c_boxbevels;
int c_edgebevels;
int c_areaportals;
int c_detail;
int c_structural;
// brushes are parsed into a temporary array of sides,
// which will have the bevels added and duplicates
// removed before the final brush is allocated
bspbrush_t *buildBrush;
void TestExpandBrushes (void);
void SetTerrainTextures( void );
void ParseTerrain( void );
/*
=============================================================================
PLANE FINDING
=============================================================================
*/
/*
================
PlaneEqual
================
*/
#define NORMAL_EPSILON 0.00001
#define DIST_EPSILON 0.01
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 qtrue;
#else
if (p->normal[0] == normal[0]
&& p->normal[1] == normal[1]
&& p->normal[2] == normal[2]
&& p->dist == dist)
return qtrue;
#endif
return qfalse;
}
/*
================
AddPlaneToHash
================
*/
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;
}
/*
================
CreateNewFloatPlane
================
*/
int CreateNewFloatPlane (vec3_t normal, vec_t dist)
{
plane_t *p, temp;
if (VectorLength(normal) < 0.5)
{
_printf( "FloatPlane: bad normal\n");
return -1;
}
// create a new plane
if (nummapplanes+2 > MAX_MAP_PLANES)
Error ("MAX_MAP_PLANES");
p = &mapplanes[nummapplanes];
VectorCopy (normal, p->normal);
p->dist = dist;
p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
VectorSubtract (vec3_origin, normal, (p+1)->normal);
(p+1)->dist = -dist;
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;
}
/*
==============
SnapVector
==============
*/
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;
}
}
}
/*
==============
SnapPlane
==============
*/
void SnapPlane (vec3_t normal, vec_t *dist)
{
SnapVector (normal);
if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
*dist = Q_rint(*dist);
}
/*
=============
FindFloatPlane
=============
*/
#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))
return i;
}
return CreateNewFloatPlane (normal, dist);
}
#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))
return p-mapplanes;
}
}
return CreateNewFloatPlane (normal, dist);
}
#endif
/*
================
MapPlaneFromPoints
================
*/
int MapPlaneFromPoints (vec3_t p0, vec3_t p1, vec3_t p2) {
vec3_t t1, t2, normal;
vec_t dist;
VectorSubtract (p0, p1, t1);
VectorSubtract (p2, p1, t2);
CrossProduct (t1, t2, normal);
VectorNormalize (normal, normal);
dist = DotProduct (p0, normal);
return FindFloatPlane (normal, dist);
}
//====================================================================
/*
===========
SetBrushContents
The contents on all sides of a brush should be the same
Sets contentsShader, contents, opaque, and detail
===========
*/
void SetBrushContents( bspbrush_t *b ) {
int contents, c2;
side_t *s;
int i;
qboolean mixed;
int allFlags;
s = &b->sides[0];
contents = s->contents;
b->contentShader = s->shaderInfo;
mixed = qfalse;
allFlags = 0;
for ( i=1 ; i<b->numsides ; i++, s++ ) {
s = &b->sides[i];
if ( !s->shaderInfo ) {
continue;
}
c2 = s->contents;
if (c2 != contents) {
mixed = qtrue;
}
allFlags |= s->surfaceFlags;
}
if ( mixed ) {
qprintf ("Entity %i, Brush %i: mixed face contents\n"
, b->entitynum, b->brushnum);
}
if ( ( contents & CONTENTS_DETAIL ) && ( contents & CONTENTS_STRUCTURAL ) ) {
_printf ("Entity %i, Brush %i: mixed CONTENTS_DETAIL and CONTENTS_STRUCTURAL\n"
, num_entities-1, entitySourceBrushes );
contents &= ~CONTENTS_DETAIL;
}
// the fulldetail flag will cause detail brushes to be
// treated like normal brushes
if ( fulldetail ) {
contents &= ~CONTENTS_DETAIL;
}
// all translucent brushes that aren't specirically made structural will
// be detail
if ( ( contents & CONTENTS_TRANSLUCENT ) && !( contents & CONTENTS_STRUCTURAL ) ) {
contents |= CONTENTS_DETAIL;
}
if ( contents & CONTENTS_DETAIL ) {
c_detail++;
b->detail = qtrue;
} else {
c_structural++;
b->detail = qfalse;
}
if ( contents & CONTENTS_TRANSLUCENT ) {
b->opaque = qfalse;
} else {
b->opaque = qtrue;
}
if ( contents & CONTENTS_AREAPORTAL ) {
c_areaportals++;
}
b->contents = contents;
}
//============================================================================
/*
=================
AddBrushBevels
Adds any additional planes necessary to allow the brush being
built to be expanded against axial bounding boxes
=================
*/
void AddBrushBevels( void ) {
int axis, dir;
int i, order;
side_t sidetemp;
side_t *s;
vec3_t normal;
float dist;
//
// 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=buildBrush->sides ; i < buildBrush->numsides ; i++,s++ ) {
if (mapplanes[s->planenum].normal[axis] == dir)
break;
}
if (i == buildBrush->numsides )
{ // add a new side
if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
Error( "MAX_BUILD_SIDES" );
}
memset( s, 0, sizeof( *s ) );
buildBrush->numsides++;
VectorClear (normal);
normal[axis] = dir;
if (dir == 1)
dist = buildBrush->maxs[axis];
else
dist = -buildBrush->mins[axis];
s->planenum = FindFloatPlane (normal, dist);
s->contents = buildBrush->sides[0].contents;
s->bevel = qtrue;
c_boxbevels++;
}
// if the plane is not in it canonical order, swap it
if (i != order)
{
sidetemp = buildBrush->sides[order];
buildBrush->sides[order] = buildBrush->sides[i];
buildBrush->sides[i] = sidetemp;
}
}
}
//
// add the edge bevels
//
if ( buildBrush->numsides == 6 ) {
return; // pure axial
} else {
int j, k, l;
float d;
winding_t *w, *w2;
side_t *s2;
vec3_t vec, vec2;
// test the non-axial plane edges
// this code tends to cause some problems...
for (i=6 ; i<buildBrush->numsides ; i++)
{
s = buildBrush->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, 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, 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 < buildBrush->numsides ; k++)
{
// if this plane has allready been used, skip it
if (PlaneEqual (&mapplanes[buildBrush->sides[k].planenum]
, normal, dist) )
break;
w2 = buildBrush->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 != buildBrush->numsides)
continue; // wasn't part of the outer hull
// add this plane
if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
Error( "MAX_BUILD_SIDES" );
}
s2 = &buildBrush->sides[buildBrush->numsides];
buildBrush->numsides++;
memset( s2, 0, sizeof( *s2 ) );
s2->planenum = FindFloatPlane (normal, dist);
s2->contents = buildBrush->sides[0].contents;
s2->bevel = qtrue;
c_edgebevels++;
}
}
}
}
}
}
/*
===============
AddBackSides
fog volumes need to have inside faces created
===============
*/
void AddBackSides( void ) {
/*
bspbrush_t *b;
int i, originalSides;
side_t *s;
side_t *newSide;
b = buildBrush;
originalSides = b->numsides;
for ( i = 0 ; i < originalSides ; i++ ) {
s = &b->sides[i];
if ( !s->shaderInfo ) {
continue;
}
if ( !(s->shaderInfo->contents & CONTENTS_FOG) ) {
continue;
}
// duplicate the up-facing side
if ( mapplanes[ s->planenum ].normal[2] == 1 ) {
newSide = &b->sides[ b->numsides ];
b->numsides++;
*newSide = *s;
newSide->backSide = qtrue;
newSide->planenum = s->planenum ^ 1; // opposite side
}
}
*/
}
/*
===============
FinishBrush
Produces a final brush based on the buildBrush->sides array
and links it to the current entity
===============
*/
bspbrush_t *FinishBrush( void ) {
bspbrush_t *b;
// liquids may need to have extra sides created for back sides
AddBackSides();
// create windings for sides and bounds for brush
if ( !CreateBrushWindings( buildBrush ) ) {
// don't keep this brush
return NULL;
}
// brushes that will not be visible at all are forced to be detail
if ( buildBrush->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
{
buildBrush->detail = qtrue;
c_detail++;
}
//
// 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 ( buildBrush->contents & CONTENTS_ORIGIN )
{
char string[32];
vec3_t origin;
if (num_entities == 1) {
_printf ("Entity %i, Brush %i: origin brushes not allowed in world\n"
, num_entities - 1, entitySourceBrushes);
return NULL;
}
VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
VectorScale (origin, 0.5, origin);
sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue (&entities[num_entities - 1], "origin", string);
VectorCopy (origin, entities[num_entities - 1].origin);
// don't keep this brush
return NULL;
}
if ( buildBrush->contents & CONTENTS_AREAPORTAL ) {
if (num_entities != 1) {
_printf ("Entity %i, Brush %i: areaportals only allowed in world\n"
, num_entities - 1, entitySourceBrushes);
return NULL;
}
}
AddBrushBevels ();
// keep it
b = CopyBrush( buildBrush );
b->entitynum = num_entities-1;
b->brushnum = entitySourceBrushes;
b->original = b;
b->next = mapent->brushes;
mapent->brushes = b;
return b;
}
//======================================================================
/*
==================
textureAxisFromPlane
==================
*/
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);
}
/*
=================
QuakeTextureVecs
Creates world-to-texture mapping vecs for crappy quake plane arrangements
=================
*/
void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2],
vec_t mappingVecs[2][4] ) {
vec3_t vecs[2];
int sv, tv;
vec_t ang, sinv, cosv;
vec_t ns, nt;
int i, j;
TextureAxisFromPlane(plane, vecs[0], vecs[1]);
if (!scale[0])
scale[0] = 1;
if (!scale[1])
scale[1] = 1;
// rotate axis
if (rotate == 0)
{ sinv = 0 ; cosv = 1; }
else if (rotate == 90)
{ sinv = 1 ; cosv = 0; }
else if (rotate == 180)
{ sinv = 0 ; cosv = -1; }
else if (rotate == 270)
{ sinv = -1 ; cosv = 0; }
else
{
ang = 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++)
mappingVecs[i][j] = vecs[i][j] / scale[i];
mappingVecs[0][3] = shift[0];
mappingVecs[1][3] = shift[1];
}
//======================================================================
/*
=================
ParseRawBrush
Just parses the sides into buildBrush->sides[], nothing else.
no validation, back plane removal, etc.
Timo - 08/26/99
added brush epairs parsing ( ignoring actually )
Timo - 08/04/99
added exclusive brush primitive parsing
Timo - 08/08/99
support for old brush format back in
NOTE : it would be "cleaner" to have seperate functions to parse between old and new brushes
=================
*/
void ParseRawBrush( ) {
side_t *side;
vec3_t planepts[3];
int planenum;
shaderInfo_t *si;
// old brushes
vec_t shift[2];
vec_t rotate;
vec_t scale[2];
char name[MAX_QPATH];
char shader[MAX_QPATH];
int flags;
buildBrush->numsides = 0;
buildBrush->detail = qfalse;
if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
MatchToken( "{" );
do
{
if (!GetToken (qtrue))
break;
if (!strcmp (token, "}") )
break;
//Timo : brush primitive : here we may have to jump over brush epairs ( only used in editor )
if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
{
do
{
if (strcmp (token, "(") )
GetToken( qfalse );
else
break;
GetToken( qtrue );
} while (1);
}
UnGetToken();
if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
Error( "MAX_BUILD_SIDES" );
}
side = &buildBrush->sides[ buildBrush->numsides ];
memset( side, 0, sizeof( *side ) );
buildBrush->numsides++;
// read the three point plane definition
Parse1DMatrix( 3, planepts[0] );
Parse1DMatrix( 3, planepts[1] );
Parse1DMatrix( 3, planepts[2] );
if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
// read the texture matrix
Parse2DMatrix( 2, 3, (float *)side->texMat );
// read the texturedef
GetToken (qfalse);
strcpy (name, token);
// save the shader name for retexturing
if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) {
Error( "MAX_MAP_BRUSHSIDES" );
}
strcpy( mapIndexedShaders[numMapIndexedShaders], name );
numMapIndexedShaders++;
if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
{
GetToken (qfalse);
shift[0] = atoi(token);
GetToken (qfalse);
shift[1] = atoi(token);
GetToken (qfalse);
rotate = atoi(token);
GetToken (qfalse);
scale[0] = atof(token);
GetToken (qfalse);
scale[1] = atof(token);
}
// find default flags and values
sprintf( shader, "textures/%s", name );
si = ShaderInfoForShader( shader );
side->shaderInfo = si;
side->surfaceFlags = si->surfaceFlags;
side->value = si->value;
side->contents = si->contents;
// allow override of default flags and values
// in Q3, the only thing you can override is DETAIL
if (TokenAvailable())
{
GetToken (qfalse);
// side->contents = atoi(token);
flags = atoi(token);
if ( flags & CONTENTS_DETAIL ) {
side->contents |= CONTENTS_DETAIL;
}
GetToken (qfalse);
// td.flags = atoi(token);
GetToken (qfalse);
// td.value = atoi(token);
}
// find the plane number
planenum = MapPlaneFromPoints (planepts[0], planepts[1], planepts[2]);
side->planenum = planenum;
if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
// get the texture mapping for this texturedef / plane combination
QuakeTextureVecs( &mapplanes[planenum], shift, rotate, scale, side->vecs );
} while (1);
if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
{
UnGetToken();
MatchToken( "}" );
MatchToken( "}" );
}
}
/*
=================
RemoveDuplicateBrushPlanes
Returns false if the brush has a mirrored set of planes,
meaning it encloses no volume.
Also removes planes without any normal
=================
*/
qboolean RemoveDuplicateBrushPlanes( bspbrush_t * b ) {
int i, j, k;
side_t *sides;
sides = b->sides;
for ( i = 1 ; i < b->numsides ; i++ ) {
// check for a degenerate plane
if ( sides[i].planenum == -1) {
_printf ("Entity %i, Brush %i: degenerate plane\n"
, b->entitynum, b->brushnum);
// remove it
for ( k = i + 1 ; k < b->numsides ; k++ ) {
sides[k-1] = sides[k];
}
b->numsides--;
i--;
continue;
}
// check for duplication and mirroring
for ( j = 0 ; j < i ; j++ ) {
if ( sides[i].planenum == sides[j].planenum ) {
_printf ("Entity %i, Brush %i: duplicate plane\n"
, b->entitynum, b->brushnum);
// remove the second duplicate
for ( k = i + 1 ; k < b->numsides ; k++ ) {
sides[k-1] = sides[k];
}
b->numsides--;
i--;
break;
}
if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
// mirror plane, brush is invalid
_printf ("Entity %i, Brush %i: mirrored plane\n"
, b->entitynum, b->brushnum);
return qfalse;
}
}
}
return qtrue;
}
/*
=================
ParseBrush
qboolean parameter to true -> parse new brush primitive format ( else use old format )
=================
*/
void ParseBrush (void) {
bspbrush_t *b;
ParseRawBrush();
buildBrush->portalareas[0] = -1;
buildBrush->portalareas[1] = -1;
buildBrush->entitynum = num_entities-1;
buildBrush->brushnum = entitySourceBrushes;
// if there are mirrored planes, the entire brush is invalid
if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
return;
}
// get the content for the entire brush
SetBrushContents( buildBrush );
// allow detail brushes to be removed
if (nodetail && (buildBrush->contents & CONTENTS_DETAIL) ) {
FreeBrush( buildBrush );
return;
}
// allow water brushes to be removed
if (nowater && (buildBrush->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) {
FreeBrush( buildBrush );
return;
}
b = FinishBrush( );
if ( !b ) {
return;
}
}
/*
================
MoveBrushesToWorld
Takes all of the brushes from the current entity and
adds them to the world's brush list.
Used by func_group
================
*/
void MoveBrushesToWorld (entity_t *mapent) {
bspbrush_t *b, *next;
parseMesh_t *pm;
// move brushes
for ( b = mapent->brushes ; b ; b = next ) {
next = b->next;
b->next = entities[0].brushes;
entities[0].brushes = b;
}
mapent->brushes = NULL;
// move patches
if ( mapent->patches ) {
for ( pm = mapent->patches ; pm->next ; pm = pm->next ) {
}
pm->next = entities[0].patches;
entities[0].patches = mapent->patches;
mapent->patches = NULL;
}
}
/*
================
AdjustBrushesForOrigin
================
*/
void AdjustBrushesForOrigin( entity_t *ent ) {
bspbrush_t *b;
int i;
side_t *s;
vec_t newdist;
parseMesh_t *p;
for ( b = ent->brushes ; b ; b = b->next ) {
for (i=0 ; i<b->numsides ; i++) {
s = &b->sides[i];
newdist = mapplanes[s->planenum].dist -
DotProduct (mapplanes[s->planenum].normal, ent->origin);
s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
}
CreateBrushWindings(b);
}
for ( p = ent->patches ; p ; p = p->next ) {
for ( i = 0 ; i < p->mesh.width*p->mesh.height ; i++ ) {
VectorSubtract( p->mesh.verts[i].xyz, ent->origin, p->mesh.verts[i].xyz );
}
}
}
/*
================
ParseMapEntity
================
*/
qboolean ParseMapEntity (void) {
epair_t *e;
if (!GetToken (qtrue))
return qfalse;
if (strcmp (token, "{") )
{
Error ("ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...", token, scriptline, entities[num_entities].origin[0], entities[num_entities].origin[1], entities[num_entities].origin[2]);
}
if (num_entities == MAX_MAP_ENTITIES)
Error ("num_entities == MAX_MAP_ENTITIES");
entitySourceBrushes = 0;
mapent = &entities[num_entities];
num_entities++;
memset (mapent, 0, sizeof(*mapent));
do
{
if (!GetToken (qtrue))
Error ("ParseEntity: EOF without closing brace");
if (!strcmp (token, "}") )
break;
if (!strcmp (token, "{") ) {
// parse a brush or patch
if (!GetToken (qtrue))
break;
if ( !strcmp( token, "patchDef2" ) ) {
numMapPatches++;
ParsePatch();
} else if ( !strcmp( token, "terrainDef" ) ) {
ParseTerrain();
} else if ( !strcmp( token, "brushDef" ) ) {
if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
Error("old brush format not allowed in new brush format map");
g_bBrushPrimit=BPRIMIT_NEWBRUSHES;
// parse brush primitive
ParseBrush();
}
else
{
if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
Error("new brush format not allowed in old brush format map");
g_bBrushPrimit=BPRIMIT_OLDBRUSHES;
// parse old brush format
UnGetToken();
ParseBrush();
}
entitySourceBrushes++;
}
else
{
// parse a key / value pair
e = ParseEpair ();
e->next = mapent->epairs;
mapent->epairs = e;
}
} while (1);
GetVectorForKey (mapent, "origin", mapent->origin);
//
// if there was an origin brush, offset all of the planes and texinfo
// for all the brushes in the entity
if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) {
AdjustBrushesForOrigin( mapent );
}
// group_info entities are just for editor grouping
// ignored
// FIXME: leak!
if (!strcmp("group_info", ValueForKey (mapent, "classname")))
{
num_entities--;
return qtrue;
}
// group entities are just for editor convenience
// toss all brushes into the world entity
if (!strcmp ("func_group", ValueForKey (mapent, "classname")))
{
if ( !strcmp ("1", ValueForKey (mapent, "terrain"))) {
SetTerrainTextures();
}
MoveBrushesToWorld (mapent);
num_entities--;
return qtrue;
}
return qtrue;
}
//===================================================================
/*
================
LoadMapFile
================
*/
void LoadMapFile (char *filename) {
bspbrush_t *b;
qprintf ("--- LoadMapFile ---\n");
_printf ("Loading map file %s\n", filename);
LoadScriptFile (filename);
num_entities = 0;
numMapDrawSurfs = 0;
c_detail = 0;
g_bBrushPrimit = BPRIMIT_UNDEFINED;
// allocate a very large temporary brush for building
// the brushes as they are loaded
buildBrush = AllocBrush( MAX_BUILD_SIDES );
while (ParseMapEntity ())
{
}
ClearBounds (map_mins, map_maxs);
for ( b = entities[0].brushes ; b ; b=b->next ) {
AddPointToBounds( b->mins, map_mins, map_maxs );
AddPointToBounds( b->maxs, map_mins, map_maxs );
}
qprintf ("%5i total world brushes\n", CountBrushList( entities[0].brushes ) );
qprintf ("%5i detail brushes\n", c_detail );
qprintf ("%5i patches\n", numMapPatches);
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]);
if ( fakemap ) {
WriteBspBrushMap ("fakemap.map", entities[0].brushes );
}
if ( testExpand ) {
TestExpandBrushes ();
}
}
//====================================================================
/*
================
TestExpandBrushes
Expands all the brush planes and saves a new map out to
allow visual inspection of the clipping bevels
================
*/
void TestExpandBrushes( void ) {
side_t *s;
int i, j;
bspbrush_t *brush, *list, *copy;
vec_t dist;
plane_t *plane;
list = NULL;
for ( brush = entities[0].brushes ; brush ; brush = brush->next ) {
copy = CopyBrush( brush );
copy->next = list;
list = copy;
// expand all the planes
for ( i=0 ; i<brush->numsides ; i++ ) {
s = brush->sides + i;
plane = &mapplanes[s->planenum];
dist = plane->dist;
for (j=0 ; j<3 ; j++) {
dist += fabs( 16 * plane->normal[j] );
}
s->planenum = FindFloatPlane( plane->normal, dist );
}
}
WriteBspBrushMap ( "expanded.map", entities[0].brushes );
Error ("can't proceed after expanding brushes");
}