quakeforge/tools/qfbsp/source/writebsp.c

478 lines
10 KiB
C
Raw Normal View History

2002-09-19 18:51:19 +00:00
/*
Copyright (C) 1996-1997 Id Software, Inc.
2002-09-19 18:51:19 +00:00
This program 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.
2002-09-19 18:51:19 +00:00
This program 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.
2002-09-19 18:51:19 +00:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2002-09-19 18:51:19 +00:00
See file, 'COPYING', for details.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include "QF/dstring.h"
#include "QF/qendian.h"
#include "QF/sys.h"
#include "bsp5.h"
2002-09-19 17:14:23 +00:00
int headclipnode;
int firstface;
/*
==================
FindFinalPlane
Used to find plane index numbers for clip nodes read from child processes
==================
*/
2002-09-19 17:14:23 +00:00
int
FindFinalPlane (dplane_t *p)
{
2002-09-19 17:14:23 +00:00
dplane_t *dplane;
2002-09-19 18:51:19 +00:00
int i;
2002-09-19 17:14:23 +00:00
for (i = 0, dplane = bsp->planes; i < bsp->numplanes; i++, dplane++) {
if (p->type != dplane->type)
continue;
if (p->dist != dplane->dist)
continue;
if (p->normal[0] != dplane->normal[0])
continue;
if (p->normal[1] != dplane->normal[1])
continue;
if (p->normal[2] != dplane->normal[2])
continue;
return i;
}
2002-09-19 17:14:23 +00:00
// new plane
if (bsp->numplanes == MAX_MAP_PLANES)
Sys_Error ("numplanes == MAX_MAP_PLANES");
BSP_AddPlane (bsp, p);
2002-09-19 17:14:23 +00:00
return bsp->numplanes - 1;
}
2002-09-19 17:14:23 +00:00
int planemapping[MAX_MAP_PLANES];
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteNodePlanes_r (node_t *node)
{
dplane_t dplane;
2002-09-19 18:51:19 +00:00
plane_t *plane;
if (node->planenum == -1)
return;
2002-09-19 17:14:23 +00:00
if (planemapping[node->planenum] == -1) { // a new plane
planemapping[node->planenum] = bsp->numplanes;
2002-09-19 17:14:23 +00:00
plane = &planes[node->planenum];
dplane.normal[0] = plane->normal[0];
dplane.normal[1] = plane->normal[1];
dplane.normal[2] = plane->normal[2];
dplane.dist = plane->dist;
dplane.type = plane->type;
BSP_AddPlane (bsp, &dplane);
}
node->outputplanenum = planemapping[node->planenum];
2002-09-19 17:14:23 +00:00
WriteNodePlanes_r (node->children[0]);
WriteNodePlanes_r (node->children[1]);
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteNodePlanes (node_t *nodes)
{
2002-09-19 17:14:23 +00:00
memset (planemapping, -1, sizeof (planemapping));
WriteNodePlanes_r (nodes);
}
//===========================================================================
2002-09-19 17:14:23 +00:00
int
2002-09-19 18:51:19 +00:00
WriteClipNodes_r (node_t *node)
{
dclipnode_t cn;
2002-09-19 18:51:19 +00:00
int num, c, i;
2002-09-19 17:14:23 +00:00
// FIXME: free more stuff?
if (node->planenum == -1) {
num = node->contents;
free (node);
return num;
}
// emit a clipnode
c = bsp->numclipnodes;
cn.planenum = node->outputplanenum;
2002-09-19 17:14:23 +00:00
for (i = 0; i < 2; i++)
cn.children[i] = WriteClipNodes_r (node->children[i]);
BSP_AddClipnode (bsp, &cn);
2002-09-19 17:14:23 +00:00
free (node);
return c;
}
/*
==================
WriteClipNodes
Called after the clipping hull is completed. Generates a disk format
representation and frees the original memory.
==================
*/
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteClipNodes (node_t *nodes)
{
headclipnode = bsp->numclipnodes;
WriteClipNodes_r (nodes);
}
//===========================================================================
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteLeaf (node_t *node)
{
dleaf_t leaf_p;
2002-09-19 18:51:19 +00:00
face_t **fp, *f;
2002-09-19 17:14:23 +00:00
// emit a leaf
leaf_p.contents = node->contents;
// write bounding box info
VectorCopy (node->mins, leaf_p.mins);
VectorCopy (node->maxs, leaf_p.maxs);
2002-09-19 17:14:23 +00:00
leaf_p.visofs = -1; // no vis info yet
2002-09-19 17:14:23 +00:00
// write the marksurfaces
leaf_p.firstmarksurface = bsp->nummarksurfaces;
2002-09-19 17:14:23 +00:00
for (fp = node->markfaces; *fp; fp++) {
// emit a marksurface
if (bsp->nummarksurfaces == MAX_MAP_MARKSURFACES)
Sys_Error ("nummarksurfaces == MAX_MAP_MARKSURFACES");
f = *fp;
2002-09-19 17:14:23 +00:00
do {
BSP_AddMarkSurface (bsp, f->outputnumber);
2002-09-19 17:14:23 +00:00
f = f->original; // grab tjunction split faces
} while (f);
}
2002-09-19 17:14:23 +00:00
leaf_p.nummarksurfaces = bsp->nummarksurfaces - leaf_p.firstmarksurface;
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteDrawNodes_r (node_t *node)
{
static dnode_t dummy;
2002-09-19 17:14:23 +00:00
dnode_t *n;
int i;
int nodenum = bsp->numnodes;
2002-09-19 17:14:23 +00:00
// emit a node
if (bsp->numnodes == MAX_MAP_NODES)
Sys_Error ("numnodes == MAX_MAP_NODES");
BSP_AddNode (bsp, &dummy);
n = &bsp->nodes[nodenum];
VectorCopy (node->mins, n->mins);
VectorCopy (node->maxs, n->maxs);
n->planenum = node->outputplanenum;
n->firstface = node->firstface;
n->numfaces = node->numfaces;
// recursively output the other nodes
2002-09-19 17:14:23 +00:00
for (i = 0; i < 2; i++) {
n = &bsp->nodes[nodenum];
2002-09-19 17:14:23 +00:00
if (node->children[i]->planenum == -1) {
if (node->children[i]->contents == CONTENTS_SOLID)
n->children[i] = -1;
2002-09-19 17:14:23 +00:00
else {
n->children[i] = -(bsp->numleafs + 1);
WriteLeaf (node->children[i]);
}
2002-09-19 17:14:23 +00:00
} else {
n->children[i] = bsp->numnodes;
WriteDrawNodes_r (node->children[i]);
}
}
}
2002-09-19 17:14:23 +00:00
void
2002-09-19 18:51:19 +00:00
WriteDrawNodes (node_t *headnode)
{
dmodel_t bm;
2002-09-19 18:51:19 +00:00
int start, i;
#if 0
if (headnode->contents < 0)
Sys_Error ("FinishBSPModel: empty model");
#endif
// emit a model
if (bsp->nummodels == MAX_MAP_MODELS)
Sys_Error ("nummodels == MAX_MAP_MODELS");
2002-09-19 17:14:23 +00:00
bm.headnode[0] = bsp->numnodes;
bm.firstface = firstface;
bm.numfaces = bsp->numfaces - firstface;
firstface = bsp->numfaces;
2002-09-19 17:14:23 +00:00
start = bsp->numleafs;
2002-09-19 17:14:23 +00:00
if (headnode->contents < 0)
WriteLeaf (headnode);
else
WriteDrawNodes_r (headnode);
bm.visleafs = bsp->numleafs - start;
2002-09-19 17:14:23 +00:00
for (i = 0; i < 3; i++) {
bm.mins[i] = headnode->mins[i] + SIDESPACE + 1; // remove the padding
bm.maxs[i] = headnode->maxs[i] - SIDESPACE - 1;
}
BSP_AddModel (bsp, &bm);
// FIXME: are all the children decendant of padded nodes?
}
/*
==================
BumpModel
Used by the clipping hull processes that only need to store headclipnode
==================
*/
2002-09-19 17:14:23 +00:00
void
BumpModel (int hullnum)
{
static dmodel_t bm;
// emit a model
bm.headnode[hullnum] = headclipnode;
BSP_AddModel (bsp, &bm);
}
//=============================================================================
2002-09-19 17:14:23 +00:00
typedef struct {
char identification[4]; // should be WAD2
int numlumps;
int infotableofs;
} wadinfo_t;
2002-09-19 17:14:23 +00:00
typedef struct {
int filepos;
int disksize;
int size; // uncompressed
char type;
char compression;
char pad1, pad2;
char name[16]; // must be null terminated
} lumpinfo_t;
2002-09-19 17:14:23 +00:00
QFile *texfile;
wadinfo_t wadinfo;
lumpinfo_t *lumpinfo;
2002-09-19 17:14:23 +00:00
void
CleanupName (char *in, char *out)
{
2002-09-19 17:14:23 +00:00
int i;
for (i = 0; i < 16; i++) {
if (!in[i])
break;
2002-09-19 17:14:23 +00:00
out[i] = toupper (in[i]);
}
2002-09-19 17:14:23 +00:00
for (; i < 16; i++)
out[i] = 0;
}
2002-09-19 17:14:23 +00:00
void
TEX_InitFromWad (char *path)
{
2002-09-19 17:14:23 +00:00
int i;
texfile = Qopen (path, "rb");
if (!texfile)
Sys_Error ("couldn't open %s", path);
2002-09-19 17:14:23 +00:00
Qread (texfile, &wadinfo, sizeof (wadinfo));
if (strncmp (wadinfo.identification, "WAD2", 4))
2002-09-19 17:14:23 +00:00
Sys_Error ("TEX_InitFromWad: %s isn't a wadfile", path);
wadinfo.numlumps = LittleLong (wadinfo.numlumps);
wadinfo.infotableofs = LittleLong (wadinfo.infotableofs);
Qseek (texfile, wadinfo.infotableofs, SEEK_SET);
2002-09-19 17:14:23 +00:00
lumpinfo = malloc (wadinfo.numlumps * sizeof (lumpinfo_t));
Qread (texfile, lumpinfo, wadinfo.numlumps * sizeof (lumpinfo_t));
for (i = 0; i < wadinfo.numlumps; i++) {
CleanupName (lumpinfo[i].name, lumpinfo[i].name);
2002-09-19 17:14:23 +00:00
lumpinfo[i].filepos = LittleLong (lumpinfo[i].filepos);
lumpinfo[i].disksize = LittleLong (lumpinfo[i].disksize);
}
}
2002-09-19 17:14:23 +00:00
int
LoadLump (char *name, dstring_t *dest)
{
2002-09-19 17:14:23 +00:00
char cname[16];
2002-09-19 18:51:19 +00:00
int i;
int ofs = dest->size;
2002-09-19 17:14:23 +00:00
CleanupName (name, cname);
2002-09-19 17:14:23 +00:00
for (i = 0; i < wadinfo.numlumps; i++) {
if (!strcmp (cname, lumpinfo[i].name)) {
dest->size += lumpinfo[i].disksize;
dstring_adjust (dest);
Qseek (texfile, lumpinfo[i].filepos, SEEK_SET);
Qread (texfile, dest->str + ofs, lumpinfo[i].disksize);
return lumpinfo[i].disksize;
}
}
2002-09-19 17:14:23 +00:00
printf ("WARNING: texture %s not found\n", name);
return 0;
}
2002-09-19 17:14:23 +00:00
void
AddAnimatingTextures (void)
{
2002-09-19 18:51:19 +00:00
int base, i, j, k;
2002-09-19 17:14:23 +00:00
char name[32];
base = nummiptex;
2002-09-19 17:14:23 +00:00
for (i = 0; i < base; i++) {
if (miptex[i][0] != '+')
continue;
strcpy (name, miptex[i]);
2002-09-19 17:14:23 +00:00
for (j = 0; j < 20; j++) {
if (j < 10)
2002-09-19 17:14:23 +00:00
name[1] = '0' + j;
else
2002-09-19 17:14:23 +00:00
name[1] = 'A' + j - 10; // alternate animation
2002-09-19 17:14:23 +00:00
// see if this name exists in the wadfile
for (k = 0; k < wadinfo.numlumps; k++)
if (!strcmp (name, lumpinfo[k].name)) {
FindMiptex (name); // add to the miptex list
break;
}
}
}
2002-09-19 17:14:23 +00:00
printf ("added %i texture frames\n", nummiptex - base);
}
2002-09-19 17:14:23 +00:00
void
WriteMiptex (void)
{
dstring_t *data;
char *path, *p;
2002-09-19 17:14:23 +00:00
char fullpath[1024];
2002-09-19 18:51:19 +00:00
dmiptexlump_t *l;
int i, len;
path = ValueForKey (&entities[0], "_wad");
2002-09-19 17:14:23 +00:00
if (!path || !path[0]) {
path = ValueForKey (&entities[0], "wad");
2002-09-19 17:14:23 +00:00
if (!path || !path[0]) {
printf ("WARNING: no wadfile specified\n");
bsp->texdatasize = 0;
return;
}
}
path = strdup (path);
p = strtok (path, ";"); // yeah yeah. but it works :)
while (p) {
sprintf (fullpath, "%s/%s", /* FIXME gamedir */ ".", path);
TEX_InitFromWad (fullpath);
AddAnimatingTextures ();
p = strtok (0, ";");
}
free (path);
2002-09-19 17:14:23 +00:00
data = dstring_new ();
data->size = sizeof (dmiptexlump_t);
dstring_adjust (data);
l = (dmiptexlump_t *) data->str;
l->nummiptex = nummiptex;
data->size = (char *) &l->dataofs[nummiptex] - data->str;
dstring_adjust (data);
l = (dmiptexlump_t *) data->str;
2002-09-19 17:14:23 +00:00
for (i = 0; i < nummiptex; i++) {
l->dataofs[i] = data->size;
len = LoadLump (miptex[i], data);
if (!len)
2002-09-19 17:14:23 +00:00
l->dataofs[i] = -1; // didn't find the texture
}
BSP_AddTextures (bsp, data->str, data->size);
}
//===========================================================================
2002-09-19 17:14:23 +00:00
void
BeginBSPFile (void)
{
static dedge_t edge;
static dleaf_t leaf;
// edge 0 is not used, because 0 can't be negated
BSP_AddEdge (bsp, &edge);
// leaf 0 is common solid with no faces
leaf.contents = CONTENTS_SOLID;
BSP_AddLeaf (bsp, &leaf);
2002-09-19 17:14:23 +00:00
firstface = 0;
}
2002-09-19 17:14:23 +00:00
void
FinishBSPFile (void)
{
QFile *f;
2002-09-19 17:14:23 +00:00
printf ("--- FinishBSPFile ---\n");
printf ("WriteBSPFile: %s\n", bspfilename);
2002-09-19 17:14:23 +00:00
WriteMiptex ();
2002-09-19 18:51:19 +00:00
// XXX PrintBSPFileSizes ();
f = Qopen (bspfilename, "wb");
WriteBSPFile (bsp, f);
Qclose (f);
}