gtkradiant/tools/quake2/q2map/writebsp.c

592 lines
11 KiB
C
Raw Normal View History

/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "qbsp.h"
int c_nofaces;
int c_facenodes;
/*
=========================================================
ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
=========================================================
*/
int planeused[MAX_MAP_PLANES];
/*
============
EmitPlanes
There is no oportunity to discard planes, because all of the original
brushes will be saved in the map.
============
*/
void EmitPlanes (void)
{
int i;
dplane_t *dp;
plane_t *mp;
int planetranslate[MAX_MAP_PLANES];
mp = mapplanes;
for (i=0 ; i<nummapplanes ; i++, mp++)
{
dp = &dplanes[numplanes];
planetranslate[i] = numplanes;
VectorCopy ( mp->normal, dp->normal);
dp->dist = mp->dist;
dp->type = mp->type;
numplanes++;
}
}
//========================================================
void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
{
int i;
int facenum;
while (f->merged)
f = f->merged;
if (f->split[0])
{
EmitMarkFace (leaf_p, f->split[0]);
EmitMarkFace (leaf_p, f->split[1]);
return;
}
facenum = f->outputnumber;
if (facenum == -1)
return; // degenerate face
if (facenum < 0 || facenum >= numfaces)
Error ("Bad leafface");
for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
if (dleaffaces[i] == facenum)
break; // merged out face
if (i == numleaffaces)
{
if (numleaffaces >= MAX_MAP_LEAFFACES)
Error ("MAX_MAP_LEAFFACES");
dleaffaces[numleaffaces] = facenum;
numleaffaces++;
}
}
/*
==================
EmitLeaf
==================
*/
void EmitLeaf (node_t *node)
{
dleaf_t *leaf_p;
portal_t *p;
int s;
face_t *f;
bspbrush_t *b;
int i;
int brushnum;
// emit a leaf
if (numleafs >= MAX_MAP_LEAFS)
Error ("MAX_MAP_LEAFS");
leaf_p = &dleafs[numleafs];
numleafs++;
leaf_p->contents = node->contents;
leaf_p->cluster = node->cluster;
leaf_p->area = node->area;
//
// write bounding box info
//
VectorCopy ((short) node->mins, leaf_p->mins);
VectorCopy ((short) node->maxs, leaf_p->maxs);
//
// write the leafbrushes
//
leaf_p->firstleafbrush = numleafbrushes;
for (b=node->brushlist ; b ; b=b->next)
{
if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
Error ("MAX_MAP_LEAFBRUSHES");
brushnum = b->original - mapbrushes;
for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
if (dleafbrushes[i] == brushnum)
break;
if (i == numleafbrushes)
{
dleafbrushes[numleafbrushes] = brushnum;
numleafbrushes++;
}
}
leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
//
// write the leaffaces
//
if (leaf_p->contents & CONTENTS_SOLID)
return; // no leaffaces in solids
leaf_p->firstleafface = numleaffaces;
for (p = node->portals ; p ; p = p->next[s])
{
s = (p->nodes[1] == node);
f = p->face[s];
if (!f)
continue; // not a visible portal
EmitMarkFace (leaf_p, f);
}
leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
}
/*
==================
EmitFace
==================
*/
void EmitFace (face_t *f)
{
dface_t *df;
int i;
int e;
f->outputnumber = -1;
if (f->numpoints < 3)
{
return; // degenerated
}
if (f->merged || f->split[0] || f->split[1])
{
return; // not a final face
}
// save output number so leaffaces can use
f->outputnumber = numfaces;
if (numfaces >= MAX_MAP_FACES)
Error ("numfaces == MAX_MAP_FACES");
df = &dfaces[numfaces];
numfaces++;
// planenum is used by qlight, but not quake
df->planenum = f->planenum & (~1);
df->side = f->planenum & 1;
df->firstedge = numsurfedges;
df->numedges = f->numpoints;
df->texinfo = f->texinfo;
for (i=0 ; i<f->numpoints ; i++)
{
// e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
if (numsurfedges >= MAX_MAP_SURFEDGES)
Error ("numsurfedges == MAX_MAP_SURFEDGES");
dsurfedges[numsurfedges] = e;
numsurfedges++;
}
}
/*
============
EmitDrawingNode_r
============
*/
int EmitDrawNode_r (node_t *node)
{
dnode_t *n;
face_t *f;
int i;
if (node->planenum == PLANENUM_LEAF)
{
EmitLeaf (node);
return -numleafs;
}
// emit a node
if (numnodes == MAX_MAP_NODES)
Error ("MAX_MAP_NODES");
n = &dnodes[numnodes];
numnodes++;
VectorCopy ((short) node->mins, n->mins);
VectorCopy ((short) node->maxs, n->maxs);
planeused[node->planenum]++;
planeused[node->planenum^1]++;
if (node->planenum & 1)
Error ("WriteDrawNodes_r: odd planenum");
n->planenum = node->planenum;
n->firstface = numfaces;
if (!node->faces)
c_nofaces++;
else
c_facenodes++;
for (f=node->faces ; f ; f=f->next)
EmitFace (f);
n->numfaces = numfaces - n->firstface;
//
// recursively output the other nodes
//
for (i=0 ; i<2 ; i++)
{
if (node->children[i]->planenum == PLANENUM_LEAF)
{
n->children[i] = -(numleafs + 1);
EmitLeaf (node->children[i]);
}
else
{
n->children[i] = numnodes;
EmitDrawNode_r (node->children[i]);
}
}
return n - dnodes;
}
//=========================================================
/*
============
WriteBSP
============
*/
void WriteBSP (node_t *headnode)
{
int oldfaces;
c_nofaces = 0;
c_facenodes = 0;
Sys_FPrintf( SYS_VRB, "--- WriteBSP ---\n");
oldfaces = numfaces;
dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
EmitAreaPortals (headnode);
Sys_FPrintf( SYS_VRB, "%5i nodes with faces\n", c_facenodes);
Sys_FPrintf( SYS_VRB, "%5i nodes without faces\n", c_nofaces);
Sys_FPrintf( SYS_VRB, "%5i faces\n", numfaces-oldfaces);
}
//===========================================================
/*
============
SetModelNumbers
============
*/
void SetModelNumbers (void)
{
int i;
int models;
char value[10];
models = 1;
for (i=1 ; i<num_entities ; i++)
{
if (entities[i].numbrushes)
{
sprintf (value, "*%i", models);
models++;
SetKeyValue (&entities[i], "model", value);
}
}
}
/*
============
SetLightStyles
============
*/
#define MAX_SWITCHED_LIGHTS 32
void SetLightStyles (void)
{
int stylenum;
char *t;
entity_t *e;
int i, j;
char value[10];
char lighttargets[MAX_SWITCHED_LIGHTS][64];
// any light that is controlled (has a targetname)
// must have a unique style number generated for it
stylenum = 0;
for (i=1 ; i<num_entities ; i++)
{
e = &entities[i];
t = ValueForKey (e, "classname");
if (Q_strncasecmp (t, "light", 5))
continue;
t = ValueForKey (e, "targetname");
if (!t[0])
continue;
// find this targetname
for (j=0 ; j<stylenum ; j++)
if (!strcmp (lighttargets[j], t))
break;
if (j == stylenum)
{
if (stylenum == MAX_SWITCHED_LIGHTS)
Error ("stylenum == MAX_SWITCHED_LIGHTS");
strcpy (lighttargets[j], t);
stylenum++;
}
sprintf (value, "%i", 32 + j);
SetKeyValue (e, "style", value);
}
}
//===========================================================
/*
============
EmitBrushes
============
*/
void EmitBrushes (void)
{
int i, j, bnum, s, x;
dbrush_t *db;
mapbrush_t *b;
dbrushside_t *cp;
vec3_t normal;
vec_t dist;
int planenum;
numbrushsides = 0;
numbrushes = nummapbrushes;
for (bnum=0 ; bnum<nummapbrushes ; bnum++)
{
b = &mapbrushes[bnum];
db = &dbrushes[bnum];
db->contents = b->contents;
db->firstside = numbrushsides;
db->numsides = b->numsides;
for (j=0 ; j<b->numsides ; j++)
{
if (numbrushsides == MAX_MAP_BRUSHSIDES)
Error ("MAX_MAP_BRUSHSIDES");
cp = &dbrushsides[numbrushsides];
numbrushsides++;
cp->planenum = b->original_sides[j].planenum;
cp->texinfo = b->original_sides[j].texinfo;
}
// add any axis planes not contained in the brush to bevel off corners
for (x=0 ; x<3 ; x++)
for (s=-1 ; s<=1 ; s+=2)
{
// add the plane
VectorCopy (vec3_origin, normal);
normal[x] = (float) s;
if (s == -1)
dist = -b->mins[x];
else
dist = b->maxs[x];
planenum = FindFloatPlane (normal, dist);
for (i=0 ; i<b->numsides ; i++)
if (b->original_sides[i].planenum == planenum)
break;
if (i == b->numsides)
{
if (numbrushsides >= MAX_MAP_BRUSHSIDES)
Error ("MAX_MAP_BRUSHSIDES");
dbrushsides[numbrushsides].planenum = planenum;
dbrushsides[numbrushsides].texinfo =
dbrushsides[numbrushsides-1].texinfo;
numbrushsides++;
db->numsides++;
}
}
}
}
//===========================================================
/*
==================
BeginBSPFile
==================
*/
void BeginBSPFile (void)
{
// these values may actually be initialized
// if the file existed when loaded, so clear them explicitly
nummodels = 0;
numfaces = 0;
numnodes = 0;
numbrushsides = 0;
numvertexes = 0;
numleaffaces = 0;
numleafbrushes = 0;
numsurfedges = 0;
// edge 0 is not used, because 0 can't be negated
numedges = 1;
// leave vertex 0 as an error
numvertexes = 1;
// leave leaf 0 as an error
numleafs = 1;
dleafs[0].contents = CONTENTS_SOLID;
}
/*
============
EndBSPFile
============
*/
void EndBSPFile (void)
{
char path[1024];
#if 0
int len;
byte *buf;
#endif
EmitBrushes ();
EmitPlanes ();
UnparseEntities ();
// load the pop
#if 0
sprintf (path, "%s/pics/pop.lmp", gamedir);
len = LoadFile (path, &buf);
memcpy (dpop, buf, sizeof(dpop));
free (buf);
#endif
// write the map
sprintf (path, "%s.bsp", source);
Sys_Printf ("Writing %s\n", path);
WriteBSPFile (path);
}
/*
==================
BeginModel
==================
*/
int firstmodleaf;
extern int firstmodeledge;
extern int firstmodelface;
void BeginModel (void)
{
dmodel_t *mod;
int start, end;
mapbrush_t *b;
int j;
entity_t *e;
vec3_t mins, maxs;
if (nummodels == MAX_MAP_MODELS)
Error ("MAX_MAP_MODELS");
mod = &dmodels[nummodels];
mod->firstface = numfaces;
firstmodleaf = numleafs;
firstmodeledge = numedges;
firstmodelface = numfaces;
//
// bound the brushes
//
e = &entities[entity_num];
start = e->firstbrush;
end = start + e->numbrushes;
ClearBounds (mins, maxs);
for (j=start ; j<end ; j++)
{
b = &mapbrushes[j];
if (!b->numsides)
continue; // not a real brush (origin brush)
AddPointToBounds (b->mins, mins, maxs);
AddPointToBounds (b->maxs, mins, maxs);
}
VectorCopy (mins, mod->mins);
VectorCopy (maxs, mod->maxs);
}
/*
==================
EndModel
==================
*/
void EndModel (void)
{
dmodel_t *mod;
mod = &dmodels[nummodels];
mod->numfaces = numfaces - mod->firstface;
nummodels++;
}